From ff301604a7dfae7625c95b5a3e871d225bd7e540 Mon Sep 17 00:00:00 2001 From: at-susie Date: Wed, 17 Jun 2026 10:26:08 +0200 Subject: [PATCH] chore: Add components overview page --- package.json | 3 + pages/components-overview/board-section.tsx | 52 +++ .../buttons-inputs-dropdowns.tsx | 361 ++++++++++++++++++ pages/components-overview/charts.tsx | 155 ++++++++ pages/components-overview/chat.tsx | 119 ++++++ .../components-overview/code-view-section.tsx | 26 ++ pages/components-overview/component-data.tsx | 143 +++++++ .../components-overview.page.tsx | 67 ++++ pages/components-overview/form-controls.tsx | 144 +++++++ pages/components-overview/kvp-form.tsx | 132 +++++++ .../navigation-components.tsx | 93 +++++ .../overlays-and-patterns.tsx | 83 ++++ .../components-overview/status-components.tsx | 249 ++++++++++++ pages/components-overview/table-and-cards.tsx | 184 +++++++++ pages/components-overview/typography.tsx | 114 ++++++ pages/components-overview/utils.tsx | 39 ++ pages/webpack.config.base.cjs | 1 + 17 files changed, 1965 insertions(+) create mode 100644 pages/components-overview/board-section.tsx create mode 100644 pages/components-overview/buttons-inputs-dropdowns.tsx create mode 100644 pages/components-overview/charts.tsx create mode 100644 pages/components-overview/chat.tsx create mode 100644 pages/components-overview/code-view-section.tsx create mode 100644 pages/components-overview/component-data.tsx create mode 100644 pages/components-overview/components-overview.page.tsx create mode 100644 pages/components-overview/form-controls.tsx create mode 100644 pages/components-overview/kvp-form.tsx create mode 100644 pages/components-overview/navigation-components.tsx create mode 100644 pages/components-overview/overlays-and-patterns.tsx create mode 100644 pages/components-overview/status-components.tsx create mode 100644 pages/components-overview/table-and-cards.tsx create mode 100644 pages/components-overview/typography.tsx create mode 100644 pages/components-overview/utils.tsx diff --git a/package.json b/package.json index 368682e67d..ac9888bb64 100644 --- a/package.json +++ b/package.json @@ -53,8 +53,11 @@ "devDependencies": { "@babel/core": "^7.23.7", "@babel/plugin-syntax-typescript": "^7.23.3", + "@cloudscape-design/board-components": "^3.0.191", "@cloudscape-design/browser-test-tools": "^3.0.0", "@cloudscape-design/build-tools": "github:cloudscape-design/build-tools#main", + "@cloudscape-design/chat-components": "^1.0.140", + "@cloudscape-design/code-view": "^3.0.142", "@cloudscape-design/documenter": "^1.0.0", "@cloudscape-design/global-styles": "^1.0.0", "@cloudscape-design/jest-preset": "^2.0.0", diff --git a/pages/components-overview/board-section.tsx b/pages/components-overview/board-section.tsx new file mode 100644 index 0000000000..7dc311becd --- /dev/null +++ b/pages/components-overview/board-section.tsx @@ -0,0 +1,52 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + +import Board from '@cloudscape-design/board-components/board'; +import BoardItem from '@cloudscape-design/board-components/board-item'; + +import Header from '~components/header'; +import SpaceBetween from '~components/space-between'; + +import { Section } from './utils'; + +export default function BoardSection() { + return ( +
+ ( + {item.data.title}} + i18nStrings={{ + dragHandleAriaLabel: 'Drag handle', + dragHandleAriaDescription: 'Use arrow keys to move, space to confirm, escape to cancel', + resizeHandleAriaLabel: 'Resize handle', + resizeHandleAriaDescription: 'Use arrow keys to resize, space to confirm, escape to cancel', + }} + > + {item.data.content} + + )} + onItemsChange={() => {}} + empty="No items" + i18nStrings={{ + liveAnnouncementDndStarted: () => '', + liveAnnouncementDndItemReordered: () => '', + liveAnnouncementDndItemResized: () => '', + liveAnnouncementDndItemInserted: () => '', + liveAnnouncementDndCommitted: () => '', + liveAnnouncementDndDiscarded: () => '', + liveAnnouncementItemRemoved: () => '', + navigationAriaLabel: 'Board navigation', + navigationAriaDescription: 'Click on non-empty item to move focus', + navigationItemAriaLabel: () => '', + }} + /> +
+ ); +} diff --git a/pages/components-overview/buttons-inputs-dropdowns.tsx b/pages/components-overview/buttons-inputs-dropdowns.tsx new file mode 100644 index 0000000000..da888b93f9 --- /dev/null +++ b/pages/components-overview/buttons-inputs-dropdowns.tsx @@ -0,0 +1,361 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React, { useState } from 'react'; + +import Autosuggest from '~components/autosuggest'; +import Box from '~components/box'; +import Button from '~components/button'; +import ButtonDropdown from '~components/button-dropdown'; +import ButtonGroup from '~components/button-group'; +import Container from '~components/container'; +import DatePicker from '~components/date-picker'; +import DateRangePicker, { DateRangePickerProps } from '~components/date-range-picker'; +import Grid from '~components/grid'; +import Header from '~components/header'; +import Modal from '~components/modal'; +import Multiselect, { MultiselectProps } from '~components/multiselect'; +import Popover from '~components/popover'; +import PropertyFilter, { PropertyFilterProps } from '~components/property-filter'; +import SegmentedControl from '~components/segmented-control'; +import SpaceBetween from '~components/space-between'; +import StatusIndicator from '~components/status-indicator'; +import ToggleButton from '~components/toggle-button'; + +import { generateDropdownOptions } from './component-data'; + +function Buttons() { + const [selectedSegment, setSelectedSegment] = useState('seg-1'); + const [toggle1, setToggle1] = useState(true); + const [toggle2, setToggle2] = useState(false); + const [toggle3, setToggle3] = useState(false); + const [toggle4, setToggle4] = useState(true); + const [toggle5, setToggle5] = useState(false); + const [toggle6, setToggle6] = useState(true); + + return ( + + + + + + + + + + + + + + setModalVisible(false)} + header="Delete resource" + footer={ + + + + + + + } + > + Are you sure you want to delete this resource? This action cannot be undone. + + + + + + + ); +} diff --git a/pages/components-overview/charts.tsx b/pages/components-overview/charts.tsx new file mode 100644 index 0000000000..ae59823aa5 --- /dev/null +++ b/pages/components-overview/charts.tsx @@ -0,0 +1,155 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + +import BarChart from '~components/bar-chart'; +import Box from '~components/box'; +import Button from '~components/button'; +import ColumnLayout from '~components/column-layout'; +import LineChart from '~components/line-chart'; +import PieChart from '~components/pie-chart'; + +import { Section, SubSection } from './utils'; + +const formatLargeNumber = (value: number): string => { + if (Math.abs(value) >= 1e6) { + return (value / 1e6).toFixed(1).replace(/\.0$/, '') + 'M'; + } + if (Math.abs(value) >= 1e3) { + return (value / 1e3).toFixed(1).replace(/\.0$/, '') + 'K'; + } + return value.toFixed(2); +}; + +const formatDateTick = (date: Date): string => + date + .toLocaleDateString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: false }) + .split(',') + .join('\n'); + +const emptyState = ( + + No data available + + There is no data available + + +); + +const noMatchState = ( + + No matching data + + There is no matching data to display + + + +); + +const barChartDates = [ + new Date(1601071200000), + new Date(1601078400000), + new Date(1601085600000), + new Date(1601092800000), + new Date(1601100000000), +]; + +const lineChartDomain: [Date, Date] = [new Date(1600984800000), new Date(1601013600000)]; + +const lineChartSite1 = [ + { x: new Date(1600984800000), y: 58020 }, + { x: new Date(1600988400000), y: 125021 }, + { x: new Date(1600992000000), y: 274021 }, + { x: new Date(1600995600000), y: 257306 }, + { x: new Date(1600999200000), y: 486039 }, + { x: new Date(1601002800000), y: 298028 }, + { x: new Date(1601006400000), y: 102839 }, + { x: new Date(1601010000000), y: 183570 }, + { x: new Date(1601013600000), y: 293910 }, +]; + +const lineChartSite2 = [ + { x: new Date(1600984800000), y: 151023 }, + { x: new Date(1600988400000), y: 149130 }, + { x: new Date(1600992000000), y: 154091 }, + { x: new Date(1600995600000), y: 181635 }, + { x: new Date(1600999200000), y: 220516 }, + { x: new Date(1601002800000), y: 172331 }, + { x: new Date(1601006400000), y: 194091 }, + { x: new Date(1601010000000), y: 179130 }, + { x: new Date(1601013600000), y: 157299 }, +]; + +export default function Charts() { + return ( +
+ + + ({ x, y: [12, 18, 15, 9, 18][i] })) }, + { title: 'Moderate', type: 'bar', data: barChartDates.map((x, i) => ({ x, y: [8, 11, 12, 11, 13][i] })) }, + { title: 'Low', type: 'bar', data: barChartDates.map((x, i) => ({ x, y: [7, 9, 8, 7, 5][i] })) }, + { + title: 'Unclassified', + type: 'bar', + data: barChartDates.map((x, i) => ({ x, y: [14, 8, 6, 4, 6][i] })), + }, + ]} + xDomain={barChartDates} + yDomain={[0, 50]} + i18nStrings={{ xTickFormatter: formatDateTick }} + ariaLabel="Stacked bar chart" + height={300} + stackedBars={true} + xTitle="Time (UTC)" + yTitle="Error count" + empty={emptyState} + noMatch={noMatchState} + /> + + + + + + + + [ + { key: 'Resource count', value: datum.value }, + { key: 'Percentage', value: `${((datum.value / sum) * 100).toFixed(0)}%` }, + { key: 'Last update on', value: datum.lastUpdate }, + ]} + segmentDescription={(datum, sum) => `${datum.value} units, ${((datum.value / sum) * 100).toFixed(0)}%`} + ariaDescription="Pie chart showing resource states." + ariaLabel="Pie chart" + empty={emptyState} + noMatch={noMatchState} + /> + + +
+ ); +} diff --git a/pages/components-overview/chat.tsx b/pages/components-overview/chat.tsx new file mode 100644 index 0000000000..08e89dd6cf --- /dev/null +++ b/pages/components-overview/chat.tsx @@ -0,0 +1,119 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React, { useState } from 'react'; + +import Avatar from '@cloudscape-design/chat-components/avatar'; +import ChatBubble from '@cloudscape-design/chat-components/chat-bubble'; +import SupportPromptGroup from '@cloudscape-design/chat-components/support-prompt-group'; + +import Box from '~components/box'; +import ButtonGroup from '~components/button-group'; +import FileTokenGroup from '~components/file-token-group'; +import Grid from '~components/grid'; +import PromptInput from '~components/prompt-input'; +import SpaceBetween from '~components/space-between'; + +import { Section } from './utils'; + +export default function Chat() { + const [value, setValue] = useState(''); + const [files, setFiles] = React.useState([ + new File([new Blob(['Test content'])], 'file-1.pdf', { type: 'application/pdf', lastModified: 1590962400000 }), + new File([new Blob(['Test content'])], 'file-2.pdf', { type: 'application/pdf', lastModified: 1590962400000 }), + ]); + return ( +
+ + + } + ariaLabel="Jane Doe message" + type="outgoing" + > + This is an outgoing message from a user. + + } + ariaLabel="AI Assistant message" + type="incoming" + > + This is an incoming message from the AI assistant. + + + } + > + Generating response + + + + + {}} + /> + + + setValue(detail.value)} + value={value} + actionButtonAriaLabel="Send message" + actionButtonIconName="send" + disableSecondaryActionsPaddings={true} + placeholder="Ask a question" + ariaLabel="Prompt input with files" + secondaryActions={ + + + + } + secondaryContent={ + ({ file }))} + onDismiss={({ detail }) => setFiles(f => f.filter((_, i) => i !== detail.fileIndex))} + alignment="horizontal" + showFileSize={true} + showFileLastModified={true} + showFileThumbnail={true} + i18nStrings={{ + removeFileAriaLabel: () => 'Remove file', + limitShowFewer: 'Show fewer files', + limitShowMore: 'Show more files', + errorIconAriaLabel: 'Error', + warningIconAriaLabel: 'Warning', + }} + /> + } + /> + +
+ ); +} diff --git a/pages/components-overview/code-view-section.tsx b/pages/components-overview/code-view-section.tsx new file mode 100644 index 0000000000..7809b3f8c7 --- /dev/null +++ b/pages/components-overview/code-view-section.tsx @@ -0,0 +1,26 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + +import CodeView from '@cloudscape-design/code-view/code-view'; + +import { Section } from './utils'; + +const codeSnippet = `import { applyTheme } from '@cloudscape-design/components/theming'; + +applyTheme({ + theme: { + tokens: { + colorBackgroundLayoutMain: '#f5f5f5', + borderRadiusContainer: '8px', + }, + }, +});`; + +export default function CodeViewSection() { + return ( +
+ +
+ ); +} diff --git a/pages/components-overview/component-data.tsx b/pages/components-overview/component-data.tsx new file mode 100644 index 0000000000..4d1948a093 --- /dev/null +++ b/pages/components-overview/component-data.tsx @@ -0,0 +1,143 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + +import { FlashbarProps } from '~components/flashbar'; +import { MultiselectProps } from '~components/multiselect'; +import ProgressBar from '~components/progress-bar'; +import { SelectProps } from '~components/select'; + +let seed = 1; +export default function pseudoRandom() { + const x = Math.sin(seed++) * 10000; + return x - Math.floor(x); +} + +export interface RandomData { + description: string; + name: string; + amount: string; + increase: boolean; +} + +const collectionData: RandomData[] = [ + { + description: 'volutpat. Nulla dignissim. Maecenas ornare egestas ligula.', + name: 'Velit Egestas LLP', + amount: '$68.54', + increase: true, + }, + { + description: 'vestibulum lorem, sit amet ultricies sem magna nec quam.', + name: 'Mattis Velit Justo Company', + amount: '$80.38', + increase: true, + }, + { + description: 'aliquet odio. Etiam ligula tortor, dictum eu, placerat eget.', + name: 'Tempor LLP', + amount: '$1.66', + increase: false, + }, + { + description: 'ridiculus mus. Donec dignissim magna a tortor. Nunc commodo.', + name: 'Egestas Hendrerit Neque Corporation', + amount: '$31.74', + increase: true, + }, + { + description: 'Vivamus molestie dapibus ligula. Aliquam erat volutpat.', + name: 'Aenean Incorporated', + amount: '$53.61', + increase: true, + }, + { + description: 'Cras sed leo. Cras vehicula aliquet libero. Integer in magna.', + name: 'Proin Ltd', + amount: '$42.19', + increase: false, + }, + { + description: 'Phasellus at augue id ante dictum cursus. Nunc mauris elit.', + name: 'Nulla Facilisi Foundation', + amount: '$97.03', + increase: true, + }, + { + description: 'Sed nec metus facilisis lorem tristique aliquet.', + name: 'Donec Vitae Corp', + amount: '$15.88', + increase: false, + }, +]; + +export const cardItems = collectionData.slice(0, 2); +export const tableItems = collectionData; + +export const flashbarItems: FlashbarProps.MessageDefinition[] = [ + { + header: 'Success', + type: 'success', + content: 'This is a success message.', + dismissible: true, + dismissLabel: 'Dismiss success message', + id: 'success', + }, + { + header: 'Warning', + type: 'warning', + content: 'This is a warning message.', + dismissible: true, + dismissLabel: 'Dismiss warning message', + id: 'warning', + }, + { + header: 'Error', + type: 'error', + content: 'This is an error message.', + dismissible: true, + dismissLabel: 'Dismiss error message', + id: 'error', + }, + { + header: 'Info', + type: 'info', + content: 'This is an info message.', + dismissible: true, + dismissLabel: 'Dismiss info message', + id: 'info', + }, + { + header: 'In-progress', + type: 'in-progress', + content: ( + <> + This is an in-progress flash. + + + ), + dismissible: true, + dismissLabel: 'Dismiss in-progress message', + id: 'in-progress', + }, + { + header: 'Loading', + type: 'in-progress', + loading: true, + content: 'This is a loading flash.', + dismissible: true, + dismissLabel: 'Dismiss loading message', + id: 'loading', + }, +]; + +export const generateDropdownOptions = (count = 25): SelectProps.Options | MultiselectProps.Options => { + return [...Array(count).keys()].map(n => { + const numberToDisplay = (n + 1).toString(); + const baseOption = { id: numberToDisplay, value: numberToDisplay, label: `Option ${numberToDisplay}` }; + if (n === 0 || n === 24 || n === 49) { + return { ...baseOption, disabled: true, disabledReason: 'disabled reason' }; + } + return baseOption; + }); +}; diff --git a/pages/components-overview/components-overview.page.tsx b/pages/components-overview/components-overview.page.tsx new file mode 100644 index 0000000000..833f3658f4 --- /dev/null +++ b/pages/components-overview/components-overview.page.tsx @@ -0,0 +1,67 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + +import Box from '~components/box'; +import SpaceBetween from '~components/space-between'; + +import BoardSection from './board-section'; +import ButtonsInputsDropdowns from './buttons-inputs-dropdowns'; +import Charts from './charts'; +import Chat from './chat'; +import CodeViewSection from './code-view-section'; +import FormControls from './form-controls'; +import KvpForm from './kvp-form'; +import NavigationComponents from './navigation-components'; +import OverlaysAndPatterns from './overlays-and-patterns'; +import StatusComponents from './status-components'; +import TableAndCards from './table-and-cards'; +import Typography from './typography'; + +export default function ComponentsOverviewPage() { + return ( +
+ + + Components overview page + +
+ +
+
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ ); +} diff --git a/pages/components-overview/form-controls.tsx b/pages/components-overview/form-controls.tsx new file mode 100644 index 0000000000..6790958d04 --- /dev/null +++ b/pages/components-overview/form-controls.tsx @@ -0,0 +1,144 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + +import Checkbox from '~components/checkbox'; +import ColumnLayout from '~components/column-layout'; +import Grid from '~components/grid'; +import RadioGroup from '~components/radio-group'; +import Slider from '~components/slider'; +import SpaceBetween from '~components/space-between'; +import Tiles from '~components/tiles'; +import Toggle from '~components/toggle'; + +import { Section } from './utils'; + +export default function FormControls() { + return ( +
+ + + + + Checked + + + Unchecked + + + Disabled + + + Disabled + + + Read-only + + + Read-only + + + + + + + + + + + + Checked + + + Unchecked + + + Disabled + + + Disabled + + + Read-only + + + Read-only + + + + + + + + + + + + + + + + + + + + +
+ ); +} diff --git a/pages/components-overview/kvp-form.tsx b/pages/components-overview/kvp-form.tsx new file mode 100644 index 0000000000..120b658c96 --- /dev/null +++ b/pages/components-overview/kvp-form.tsx @@ -0,0 +1,132 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + +import Box from '~components/box'; +import Button from '~components/button'; +import ColumnLayout from '~components/column-layout'; +import CopyToClipboard from '~components/copy-to-clipboard'; +import Form from '~components/form'; +import FormField from '~components/form-field'; +import Grid from '~components/grid'; +import Header from '~components/header'; +import Icon from '~components/icon'; +import Input from '~components/input'; +import KeyValuePairs from '~components/key-value-pairs'; +import Link from '~components/link'; +import SpaceBetween from '~components/space-between'; +import StatusIndicator from '~components/status-indicator'; +import Textarea from '~components/textarea'; +import Tiles from '~components/tiles'; + +import { Section } from './utils'; + +export default function KvpForm() { + const [value, setValue] = React.useState('standard'); + return ( +
+ + + +
General configuration
+ + Info + + ), + }, + { label: 'Status', value: Available }, + { label: 'Price class', value: 'Use only US, Canada, Europe, and Asia' }, + { label: 'CNAMEs', value: example.com }, + { + label: 'ARN', + value: ( + + ), + }, + ]} + /> +
+
+ + + +
+ + + + } + header={
Create instance
} + > + + + + + setValue(e.detail.value)} + columns={4} + items={[ + { + value: 'standard', + label: 'Standard', + description: 'Recommended for most workloads', + image: , + }, + { + value: 'optimized', + label: 'Optimized', + description: 'Best for dynamic content', + image: , + }, + { + value: 'custom', + label: 'Custom', + description: 'Configure your own policy', + image: , + }, + { + value: 'disabled', + label: 'Disabled', + description: 'No caching applied', + image: , + }, + ]} + ariaLabel="Cache policy" + /> + + + + + + + + +