Merge branch 'master' into canon-docs-updates

This commit is contained in:
Charles de Dreuille
2025-04-14 12:53:45 +02:00
65 changed files with 2769 additions and 775 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/canon': minor
---
Improve class name structure using data attributes instead of class names.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/canon': patch
---
Add new DataTable component and update Table component styles.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/repo-tools': patch
---
Checking up the files where `createRouter` has been declared and check if `@deprecated` tag exists. If it does not exist then only the message will appear.
Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

+237 -95
View File
@@ -29,8 +29,100 @@ Here's an example of how your theme.css file should look like:
<CodeBlock lang="css" code={customTheme} />
## CSS class name structure
All Canon components come with a set of CSS classes that you can use to style them. To make it
easier to identify the class name you can use, we use a specific structure for the class names.
<img
src="/css-classname-structure.png"
style={{ width: '100%', marginBottom: '32px', marginTop: '16px' }}
/>
Every component has a unique prefix `.canon-` followed by the component name. Component props
are represented using the `data-` attribute. That way, class names are easily identifiable.
## Available CSS variables
### Base colors
These colors are used for special purposes like ring, scrollbar, ...
<Table.Root>
<Table.Header>
<Table.HeaderRow>
<Table.HeaderCell>Prop</Table.HeaderCell>
<Table.HeaderCell>Description</Table.HeaderCell>
</Table.HeaderRow>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
<Chip head>--canon-black</Chip>
</Table.Cell>
<Table.Cell>
Pure black color. This one should be the same in light and dark themes.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-white</Chip>
</Table.Cell>
<Table.Cell>
Pure white color. This one should be the same in light and dark themes.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-gray-1</Chip>
</Table.Cell>
<Table.Cell>You can use these mostly for backgrounds colors.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-gray-2</Chip>
</Table.Cell>
<Table.Cell>You can use these mostly for backgrounds colors.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-gray-3</Chip>
</Table.Cell>
<Table.Cell>You can use these mostly for backgrounds colors.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-gray-4</Chip>
</Table.Cell>
<Table.Cell>You can use these mostly for backgrounds colors.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-gray-5</Chip>
</Table.Cell>
<Table.Cell>You can use these mostly for backgrounds colors.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-gray-6</Chip>
</Table.Cell>
<Table.Cell>You can use these mostly for backgrounds colors.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-gray-7</Chip>
</Table.Cell>
<Table.Cell>You can use these mostly for backgrounds colors.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-gray-8</Chip>
</Table.Cell>
<Table.Cell>You can use these mostly for backgrounds colors.</Table.Cell>
</Table.Row>
</Table.Body>
</Table.Root>
### Core background colors
These colors are used for the background of your application. We are mostly using for now a
@@ -57,98 +149,60 @@ color of your app.
</Table.Cell>
<Table.Cell>Use for any panels or elevated surfaces.</Table.Cell>
</Table.Row>
</Table.Body>
</Table.Root>
### Component background colors
These colors are meants for interactive components like buttons, callout avatar, chips, ...
We have two main level of background you can customise. The accent one is mostly used as a primary option
for interactive components. The tint one is used for secondary components like callouts, avatars, ...
<Table.Root>
<Table.Header>
<Table.HeaderRow>
<Table.HeaderCell>Prop</Table.HeaderCell>
<Table.HeaderCell>Description</Table.HeaderCell>
</Table.HeaderRow>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
<Chip head>--canon-bg-accent</Chip>
<Chip head>--canon-bg-surface-2</Chip>
</Table.Cell>
<Table.Cell>Used mostly for primary interactive components.</Table.Cell>
<Table.Cell>Use for any panels or elevated surfaces.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-bg-accent-hover</Chip>
</Table.Cell>
<Table.Cell>
Used for interactive components when they are hovered.
<Chip head>--canon-bg-solid</Chip>
</Table.Cell>
<Table.Cell>Used for solid background colors.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-bg-accent-focus</Chip>
</Table.Cell>
<Table.Cell>
Used for interactive components when they are in focus.
<Chip head>--canon-bg-solid-hover</Chip>
</Table.Cell>
<Table.Cell>Used for solid background colors when hovered.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-bg-accent-disabled</Chip>
<Chip head>--canon-bg-solid-pressed</Chip>
</Table.Cell>
<Table.Cell>Used for solid background colors when pressed.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
Used for interactive components when they are disabled.
<Chip head>--canon-bg-solid-disabled</Chip>
</Table.Cell>
<Table.Cell>Used for solid background colors when disabled.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-bg-tint</Chip>
</Table.Cell>
<Table.Cell>Used for secondary interactive components.</Table.Cell>
<Table.Cell>Used for tint background colors.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-bg-tint-hover</Chip>
</Table.Cell>
<Table.Cell>
Used for interactive components when they are hovered.
</Table.Cell>
<Table.Cell>Used for tint background colors when hovered.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-bg-tint-focus</Chip>
</Table.Cell>
<Table.Cell>
Used for interactive components when they are active.
</Table.Cell>
<Table.Cell>Used for tint background colors when active.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-bg-tint-disabled</Chip>
</Table.Cell>
<Table.Cell>
Used for interactive components when they are disabled.
</Table.Cell>
<Table.Cell>Used for tint background colors when disabled.</Table.Cell>
</Table.Row>
</Table.Body>
</Table.Root>
### Status background colors
These backgrounds are intended for non-interactive elements to visually convey a specific state.
<Table.Root>
<Table.Header>
<Table.HeaderRow>
<Table.HeaderCell>Prop</Table.HeaderCell>
<Table.HeaderCell>Description</Table.HeaderCell>
</Table.HeaderRow>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
<Chip head>--canon-bg-danger</Chip>
@@ -189,7 +243,7 @@ are prefixed with `fg` to make it easier to identify.
<Chip head>--canon-fg-primary</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of `--canon-bg-app` or `--canon-bg-surface-1`.
It should be used on top of main background surfaces.
</Table.Cell>
</Table.Row>
<Table.Row>
@@ -197,7 +251,7 @@ are prefixed with `fg` to make it easier to identify.
<Chip head>--canon-fg-secondary</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of `--canon-bg-app` or `--canon-bg-surface-1`.
It should be used on top of main background surfaces.
</Table.Cell>
</Table.Row>
<Table.Row>
@@ -205,7 +259,7 @@ are prefixed with `fg` to make it easier to identify.
<Chip head>--canon-fg-link</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of `--canon-bg-app` or `--canon-bg-surface-1`.
It should be used on top of main background surfaces.
</Table.Cell>
</Table.Row>
<Table.Row>
@@ -213,38 +267,64 @@ are prefixed with `fg` to make it easier to identify.
<Chip head>--canon-fg-link-hover</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of `--canon-bg-app` or `--canon-bg-surface-1`.
It should be used on top of main background surfaces.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-fg-accent</Chip>
<Chip head>--canon-fg-disabled</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of main background surfaces.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-fg-solid</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of solid background colors.
</Table.Cell>
<Table.Cell>It should be used on top of `--canon-bg-accent`.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-fg-tint</Chip>
</Table.Cell>
<Table.Cell>It should be used on top of `--canon-bg-tint`.</Table.Cell>
<Table.Cell>
It should be used on top of tint background colors.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-fg-tint-disabled</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of tint background colors when disabled.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-fg-danger</Chip>
</Table.Cell>
<Table.Cell>It should be used on top of `--canon-bg-danger`.</Table.Cell>
<Table.Cell>
It should be used on top of danger background colors.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-fg-warning</Chip>
</Table.Cell>
<Table.Cell>It should be used on top of `--canon-bg-warning`.</Table.Cell>
<Table.Cell>
It should be used on top of warning background colors.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-fg-success</Chip>
</Table.Cell>
<Table.Cell>It should be used on top of `--canon-bg-success`.</Table.Cell>
<Table.Cell>
It should be used on top of success background colors.
</Table.Cell>
</Table.Row>
</Table.Body>
</Table.Root>
@@ -288,21 +368,9 @@ low contrast to help as a separator with the different background colors.
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-border-tint</Chip>
<Chip head>--canon-border-disabled</Chip>
</Table.Cell>
<Table.Cell>It should be used on top of `--canon-bg-tint`.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-border-tint-hover</Chip>
</Table.Cell>
<Table.Cell>Used when the component is hovered.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-border-tint-focus</Chip>
</Table.Cell>
<Table.Cell>Used when the component is in focus.</Table.Cell>
<Table.Cell>Used when the component is disabled.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
@@ -325,7 +393,40 @@ low contrast to help as a separator with the different background colors.
</Table.Body>
</Table.Root>
## Font families
### Special colors
These colors are used for special purposes like ring, scrollbar, ...
<Table.Root>
<Table.Header>
<Table.HeaderRow>
<Table.HeaderCell>Prop</Table.HeaderCell>
<Table.HeaderCell>Description</Table.HeaderCell>
</Table.HeaderRow>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
<Chip head>--canon-ring</Chip>
</Table.Cell>
<Table.Cell>The color of the ring.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-scrollbar</Chip>
</Table.Cell>
<Table.Cell>The color of the scrollbar.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-scrollbar-thumb</Chip>
</Table.Cell>
<Table.Cell>The color of the scrollbar thumb.</Table.Cell>
</Table.Row>
</Table.Body>
</Table.Root>
### Font families
We have two fonts that we use across Canon. The first one is the sans-serif
font that we use for the body of the application. The second one is the
@@ -354,9 +455,9 @@ monospace font that we use for code blocks and tables.
</Table.Body>
</Table.Root>
## Font weights
### Font weights
We have two font weights that we use across Canon. Regular or Bold
We have two font weights that we use across Canon. Regular or Bold.
<Table.Root>
<Table.Header>
@@ -381,7 +482,7 @@ We have two font weights that we use across Canon. Regular or Bold
</Table.Body>
</Table.Root>
## Spacing
### Spacing
We built a spacing system based on a single value `--canon-space`. This value is
used to calculate the spacing for all the components. By default if you would like to
@@ -520,18 +621,9 @@ tokens for pretty much each spacing properties like padding, margin, gaps, ...
</Table.Body>
</Table.Root>
### Radius (Work in progress)
### Radius
Our radius system is a little bit different from spacing as we use a factor to calculate the radius values.
Under the hood different components uses different radius values. For example, button can be set to have full radius
while a checkbox will have to remain a square.
Here are the different radius factor we recommend to use
- None: `0`
- Small: `0.75`
- Medium: `1`
- Large: `1.5`
We use a radius system to make sure that the components have a consistent look and feel.
<Table.Root>
<Table.Header>
@@ -543,9 +635,59 @@ Here are the different radius factor we recommend to use
<Table.Body>
<Table.Row>
<Table.Cell>
<Chip head>--canon-radius-factor</Chip>
<Chip head>--canon-radius-1</Chip>
</Table.Cell>
<Table.Cell>
The radius of the component. Default value is `0.125rem`.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-radius-2</Chip>
</Table.Cell>
<Table.Cell>
The radius of the component. Default value is `0.25rem`.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-radius-3</Chip>
</Table.Cell>
<Table.Cell>
The radius of the component. Default value is `0.5rem`.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-radius-4</Chip>
</Table.Cell>
<Table.Cell>
The radius of the component. Default value is `0.75rem`.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-radius-5</Chip>
</Table.Cell>
<Table.Cell>
The radius of the component. Default value is `1rem`.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-radius-6</Chip>
</Table.Cell>
<Table.Cell>
The radius of the component. Default value is `1.25rem`.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-radius-full</Chip>
</Table.Cell>
<Table.Cell>
The radius of the component. Default value is `9999px`.
</Table.Cell>
<Table.Cell>By default the value is set to medium `1`.</Table.Cell>
</Table.Row>
</Table.Body>
</Table.Root>
+6 -6
View File
@@ -16,7 +16,7 @@
}
}
.canon-Button--variant-primary {
.canon-Button[data-variant="primary"] {
background-color: var(--canon-bg-solid);
color: var(--canon-fg-solid);
transition: background-color .15s, box-shadow .15s;
@@ -40,7 +40,7 @@
}
}
.canon-Button--variant-secondary {
.canon-Button[data-variant="secondary"] {
background-color: var(--canon-bg-surface-1);
box-shadow: inset 0 0 0 1px var(--canon-border);
color: var(--canon-fg-primary);
@@ -66,24 +66,24 @@
}
}
.canon-Button--size-medium {
.canon-Button[data-size="medium"] {
font-size: var(--canon-font-size-4);
padding: 0 var(--canon-space-3);
height: 40px;
}
.canon-Button--size-small {
.canon-Button[data-size="small"] {
font-size: var(--canon-font-size-3);
padding: 0 var(--canon-space-2);
height: 32px;
}
.canon-Button--size-small .canon-Button--icon {
.canon-Button[data-size="small"] .canon-ButtonIcon {
width: 1rem;
height: 1rem;
}
.canon-Button--size-medium .canon-Button--icon {
.canon-Button[data-size="medium"] .canon-ButtonIcon {
width: 1.5rem;
height: 1.5rem;
}
+123 -116
View File
@@ -22,7 +22,7 @@
}
}
.canon-Button--variant-primary {
.canon-Button[data-variant="primary"] {
background-color: var(--canon-bg-solid);
color: var(--canon-fg-solid);
transition: background-color .15s, box-shadow .15s;
@@ -46,7 +46,7 @@
}
}
.canon-Button--variant-secondary {
.canon-Button[data-variant="secondary"] {
background-color: var(--canon-bg-surface-1);
box-shadow: inset 0 0 0 1px var(--canon-border);
color: var(--canon-fg-primary);
@@ -72,28 +72,58 @@
}
}
.canon-Button--size-medium {
.canon-Button[data-size="medium"] {
font-size: var(--canon-font-size-4);
padding: 0 var(--canon-space-3);
height: 40px;
}
.canon-Button--size-small {
.canon-Button[data-size="small"] {
font-size: var(--canon-font-size-3);
padding: 0 var(--canon-space-2);
height: 32px;
}
.canon-Button--size-small .canon-Button--icon {
.canon-Button[data-size="small"] .canon-ButtonIcon {
width: 1rem;
height: 1rem;
}
.canon-Button--size-medium .canon-Button--icon {
.canon-Button[data-size="medium"] .canon-ButtonIcon {
width: 1.5rem;
height: 1.5rem;
}
.canon-DataTableRoot {
gap: var(--canon-space-3);
flex-direction: column;
display: flex;
}
.canon-DataTablePagination {
padding-top: var(--canon-space-5);
justify-content: space-between;
align-items: center;
display: flex;
}
.canon-DataTablePagination--left {
justify-content: space-between;
align-items: center;
display: flex;
}
.canon-DataTablePagination--right {
justify-content: space-between;
align-items: center;
gap: var(--canon-space-2);
display: flex;
}
.canon-DataTablePagination--select {
min-width: 10.5rem;
}
.canon-Flex {
display: flex;
}
@@ -179,117 +209,94 @@
display: flex;
}
.table {
background-color: var(--canon-bg-surface-1);
border-radius: var(--canon-radius-2);
.canon-TableRoot {
caption-side: bottom;
border-collapse: collapse;
width: 100%;
padding-bottom: var(--canon-space-0_5);
padding-top: var(--canon-space-0_5);
font-size: var(--canon-font-size-3);
font-family: var(--canon-font-regular);
font-weight: var(--canon-font-weight-regular);
position: relative;
overflow: auto;
& table {
caption-side: bottom;
width: 100%;
font-size: var(--canon-font-size-sm);
border-collapse: collapse;
}
}
.table-head {
.canon-TableHead {
text-align: left;
padding: var(--canon-space-3);
font-size: var(--canon-font-size-3);
color: var(--canon-fg-primary);
}
.table-body {
& tr:last-child {
border-bottom: none;
}
.canon-TableBody {
color: var(--canon-fg-primary);
}
.table-row {
.canon-TableRow {
border-bottom: 1px solid var(--canon-border);
transition: color .2s ease-in-out;
&:hover td {
background-color: var(--canon-bg-tint);
}
& .table-cell:first-child {
border-top-left-radius: var(--canon-radius-2);
border-bottom-left-radius: var(--canon-radius-2);
box-shadow: inset 4px 2px 0 0 var(--canon-bg-surface-1), inset 4px -2px 0 0 var(--canon-bg-surface-1);
padding-left: var(--canon-space-3);
}
& .table-cell:last-child {
border-top-right-radius: var(--canon-radius-2);
border-bottom-right-radius: var(--canon-radius-2);
box-shadow: inset -4px 2px 0 0 var(--canon-bg-surface-1), inset -4px -2px 0 0 var(--canon-bg-surface-1);
padding-right: var(--canon-space-3);
}
}
.table-cell {
.canon-TableBody .canon-TableRow:hover {
background-color: var(--canon-gray-2);
}
.canon-TableCell {
padding: var(--canon-space-3);
background-color: var(--canon-bg);
box-shadow: inset 0px 2px 0 0 var(--canon-bg-surface-1), inset 0px -2px 0 0 var(--canon-bg-surface-1);
font-size: var(--canon-font-size-3);
}
.canon-TableCellText, .canon-TableCellLink {
gap: var(--canon-space-0_5);
flex-direction: column;
display: flex;
}
.canon-Text {
font-family: var(--canon-font-regular);
color: var(--canon-fg-primary);
margin: 0;
padding: 0;
}
.canon-Text--variant-body {
.canon-Text[data-variant="body"] {
font-size: var(--canon-font-size-3);
line-height: 140%;
}
.canon-Text--variant-subtitle {
.canon-Text[data-variant="subtitle"] {
font-size: var(--canon-font-size-4);
line-height: 140%;
}
.canon-Text--variant-caption {
.canon-Text[data-variant="caption"] {
font-size: var(--canon-font-size-2);
line-height: 140%;
}
.canon-Text--variant-label {
.canon-Text[data-variant="label"] {
font-size: var(--canon-font-size-1);
line-height: 140%;
}
.canon-Text--weight-regular {
.canon-Text[data-weight="regular"] {
font-weight: var(--canon-font-weight-regular);
}
.canon-Text--weight-bold {
.canon-Text[data-weight="bold"] {
font-weight: var(--canon-font-weight-bold);
}
.canon-Text--color-primary {
.canon-Text[data-color="primary"] {
color: var(--canon-fg-primary);
}
.canon-Text--color-secondary {
.canon-Text[data-color="secondary"] {
color: var(--canon-fg-secondary);
}
.canon-Text--color-danger {
.canon-Text[data-color="danger"] {
color: var(--canon-fg-danger);
}
.canon-Text--color-warning {
.canon-Text[data-color="warning"] {
color: var(--canon-fg-warning);
}
.canon-Text--color-success {
.canon-Text[data-color="success"] {
color: var(--canon-fg-success);
}
@@ -301,32 +308,32 @@
line-height: 100%;
}
.canon-Heading--variant-display {
.canon-Heading[data-variant="display"] {
font-size: var(--canon-font-size-10);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title1 {
.canon-Heading[data-variant="title1"] {
font-size: var(--canon-font-size-9);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title2 {
.canon-Heading[data-variant="title2"] {
font-size: var(--canon-font-size-8);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title3 {
.canon-Heading[data-variant="title3"] {
font-size: var(--canon-font-size-7);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title4 {
.canon-Heading[data-variant="title4"] {
font-size: var(--canon-font-size-title4);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title5 {
.canon-Heading[data-variant="title5"] {
font-size: var(--canon-font-size-5);
font-weight: var(--canon-font-weight-bold);
}
@@ -349,7 +356,7 @@
}
}
.canon-IconButton--variant-primary {
.canon-IconButton[data-variant="primary"] {
background-color: var(--canon-bg-solid);
color: var(--canon-fg-solid);
transition: background-color .15s, box-shadow .15s;
@@ -373,7 +380,7 @@
}
}
.canon-IconButton--variant-secondary {
.canon-IconButton[data-variant="secondary"] {
background-color: var(--canon-bg-surface-1);
box-shadow: inset 0 0 0 1px var(--canon-border);
color: var(--canon-fg-primary);
@@ -399,24 +406,24 @@
}
}
.canon-IconButton--size-medium {
.canon-IconButton[data-size="medium"] {
font-size: var(--canon-font-size-4);
width: 40px;
height: 40px;
}
.canon-IconButton--size-small {
.canon-IconButton[data-size="small"] {
font-size: var(--canon-font-size-3);
width: 32px;
height: 32px;
}
.canon-IconButton--size-small .canon-IconButton--icon {
.canon-IconButton[data-size="small"] .canon-IconButtonIcon {
width: 1rem;
height: 1rem;
}
.canon-IconButton--size-medium .canon-IconButton--icon {
.canon-IconButton[data-size="medium"] .canon-IconButtonIcon {
width: 1.5rem;
height: 1.5rem;
}
@@ -428,14 +435,14 @@
display: flex;
}
.canon-TextField--label {
.canon-TextFieldLabel {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-primary);
margin-bottom: var(--canon-space-1_5);
}
.canon-TextField--description {
.canon-TextFieldDescription {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-secondary);
@@ -443,7 +450,7 @@
margin: 0;
}
.canon-TextField--error {
.canon-TextFieldError {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-danger);
@@ -451,7 +458,7 @@
margin: 0;
}
.canon-TextField--input {
.canon-TextFieldInput {
border-radius: var(--canon-radius-3);
border: 1px solid var(--canon-border);
padding: 0 var(--canon-space-4);
@@ -464,39 +471,39 @@
transition: border-color .2s ease-in-out, outline-color .2s ease-in-out;
}
.canon-TextField--input::placeholder {
.canon-TextFieldInput::placeholder {
color: var(--canon-fg-secondary);
}
.canon-TextField--input:hover {
.canon-TextFieldInput:hover {
border-color: var(--canon-border-hover);
}
.canon-TextField--input:focus-visible {
.canon-TextFieldInput:focus-visible {
outline-color: var(--canon-border-pressed);
border-color: var(--canon-border-pressed);
outline-width: 0;
}
.canon-TextField--input[data-invalid] {
.canon-TextFieldInput[data-invalid] {
border-color: var(--canon-fg-danger);
}
.canon-TextField--input[data-disabled] {
.canon-TextFieldInput[data-disabled] {
opacity: .5;
cursor: not-allowed;
border: 1px solid var(--canon-border-disabled);
}
.canon-TextField--input-size-small {
.canon-TextFieldInput[data-size="small"] {
height: 2rem;
}
.canon-TextField--input-size-medium {
.canon-TextFieldInput[data-size="medium"] {
height: 2.5rem;
}
.canon-TextField--required {
.canon-TextFieldRequired {
color: var(--canon-fg-secondary);
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
@@ -573,31 +580,31 @@
}
}
.canon-Link--variant-body {
.canon-Link[data-variant="body"] {
font-size: var(--canon-font-size-3);
line-height: 140%;
}
.canon-Link--variant-subtitle {
.canon-Link[data-variant="subtitle"] {
font-size: var(--canon-font-size-4);
line-height: 140%;
}
.canon-Link--variant-caption {
.canon-Link[data-variant="caption"] {
font-size: var(--canon-font-size-2);
line-height: 140%;
}
.canon-Link--variant-label {
.canon-Link[data-variant="label"] {
font-size: var(--canon-font-size-1);
line-height: 140%;
}
.canon-Link--weight-regular {
.canon-Link[data-weight="regular"] {
font-weight: var(--canon-font-weight-regular);
}
.canon-Link--weight-bold {
.canon-Link[data-weight="bold"] {
font-weight: var(--canon-font-weight-bold);
}
@@ -721,14 +728,14 @@
display: flex;
}
.canon-Select--label {
.canon-SelectLabel {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-primary);
margin-bottom: var(--canon-space-1_5);
}
.canon-Select--description {
.canon-SelectDescription {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-secondary);
@@ -736,7 +743,7 @@
margin: 0;
}
.canon-Select--error {
.canon-SelectError {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-danger);
@@ -744,7 +751,7 @@
margin: 0;
}
.canon-Select--trigger {
.canon-SelectTrigger {
box-sizing: border-box;
border-radius: var(--canon-radius-3);
border: 1px solid var(--canon-border);
@@ -763,38 +770,38 @@
display: flex;
}
.canon-Select--trigger::placeholder {
.canon-SelectTrigger::placeholder {
color: var(--canon-fg-secondary);
}
.canon-Select--trigger:hover {
.canon-SelectTrigger:hover {
border-color: var(--canon-border-hover);
}
.canon-Select--trigger:focus-visible {
.canon-SelectTrigger:focus-visible {
border-color: var(--canon-border-pressed);
outline: 0;
}
.canon-Select--trigger[data-invalid] {
.canon-SelectTrigger[data-invalid] {
border-color: var(--canon-fg-danger);
}
.canon-Select--trigger[data-invalid]:hover, .canon-Select--trigger[data-invalid]:focus-visible {
.canon-SelectTrigger[data-invalid]:hover, .canon-SelectTrigger[data-invalid]:focus-visible {
border-width: 2px;
}
.canon-Select--trigger[data-disabled] {
.canon-SelectTrigger[data-disabled] {
cursor: not-allowed;
border-color: var(--canon-border-disabled);
color: var(--canon-fg-disabled);
}
.canon-Select--trigger-size-small {
.canon-SelectTrigger[data-size="small"] {
height: 2rem;
}
.canon-Select--trigger-size-medium {
.canon-SelectTrigger[data-size="medium"] {
height: 3rem;
}
@@ -803,11 +810,11 @@
transition: transform .2s;
}
.canon-Select--trigger[data-popup-open] .canon-SelectIcon {
.canon-SelectTrigger[data-popup-open] .canon-SelectIcon {
transform: rotate(180deg);
}
.canon-Select--popup {
.canon-SelectPopup {
box-sizing: border-box;
max-height: var(--available-height);
background-color: var(--canon-bg-surface-1);
@@ -822,12 +829,12 @@
box-shadow: 0 4px 12px #0003;
}
.canon-Select--popup[data-starting-style], .canon-Select--popup[data-ending-style] {
.canon-SelectPopup[data-starting-style], .canon-SelectPopup[data-ending-style] {
opacity: 0;
transform: scale(.9);
}
.canon-Select--item {
.canon-SelectItem {
width: var(--anchor-width);
padding-block: var(--canon-space-2);
padding-inline: var(--canon-space-4);
@@ -845,13 +852,13 @@
position: relative;
}
.canon-Select--item[data-highlighted] {
.canon-SelectItem[data-highlighted] {
z-index: 0;
color: var(--canon-fg-primary);
position: relative;
}
.canon-Select--item[data-highlighted]:before {
.canon-SelectItem[data-highlighted]:before {
content: "";
z-index: -1;
background-color: var(--canon-bg-tint-hover);
@@ -861,37 +868,37 @@
inset-inline: .25rem;
}
.canon-Select--item[data-disabled] {
.canon-SelectItem[data-disabled] {
cursor: not-allowed;
color: var(--canon-fg-disabled);
}
.canon-Select--item-indicator {
.canon-SelectItemIndicator {
grid-area: icon;
justify-content: center;
align-items: center;
display: flex;
}
.canon-Select--item-text {
.canon-SelectItemText {
flex: 1;
grid-area: text;
}
.canon-Select--required {
.canon-SelectRequired {
color: var(--canon-fg-secondary);
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
margin-left: var(--canon-space-1);
}
.canon-Select--icon {
.canon-SelectIcon {
justify-content: center;
align-items: center;
display: flex;
}
.canon-Select--value {
.canon-SelectValue {
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
+23
View File
@@ -0,0 +1,23 @@
.canon-DataTablePagination {
padding-top: var(--canon-space-5);
justify-content: space-between;
align-items: center;
display: flex;
}
.canon-DataTablePagination--left {
justify-content: space-between;
align-items: center;
display: flex;
}
.canon-DataTablePagination--right {
justify-content: space-between;
align-items: center;
gap: var(--canon-space-2);
display: flex;
}
.canon-DataTablePagination--select {
min-width: 10.5rem;
}
+6 -6
View File
@@ -6,32 +6,32 @@
line-height: 100%;
}
.canon-Heading--variant-display {
.canon-Heading[data-variant="display"] {
font-size: var(--canon-font-size-10);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title1 {
.canon-Heading[data-variant="title1"] {
font-size: var(--canon-font-size-9);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title2 {
.canon-Heading[data-variant="title2"] {
font-size: var(--canon-font-size-8);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title3 {
.canon-Heading[data-variant="title3"] {
font-size: var(--canon-font-size-7);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title4 {
.canon-Heading[data-variant="title4"] {
font-size: var(--canon-font-size-title4);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title5 {
.canon-Heading[data-variant="title5"] {
font-size: var(--canon-font-size-5);
font-weight: var(--canon-font-weight-bold);
}
+6 -6
View File
@@ -16,7 +16,7 @@
}
}
.canon-IconButton--variant-primary {
.canon-IconButton[data-variant="primary"] {
background-color: var(--canon-bg-solid);
color: var(--canon-fg-solid);
transition: background-color .15s, box-shadow .15s;
@@ -40,7 +40,7 @@
}
}
.canon-IconButton--variant-secondary {
.canon-IconButton[data-variant="secondary"] {
background-color: var(--canon-bg-surface-1);
box-shadow: inset 0 0 0 1px var(--canon-border);
color: var(--canon-fg-primary);
@@ -66,24 +66,24 @@
}
}
.canon-IconButton--size-medium {
.canon-IconButton[data-size="medium"] {
font-size: var(--canon-font-size-4);
width: 40px;
height: 40px;
}
.canon-IconButton--size-small {
.canon-IconButton[data-size="small"] {
font-size: var(--canon-font-size-3);
width: 32px;
height: 32px;
}
.canon-IconButton--size-small .canon-IconButton--icon {
.canon-IconButton[data-size="small"] .canon-IconButtonIcon {
width: 1rem;
height: 1rem;
}
.canon-IconButton--size-medium .canon-IconButton--icon {
.canon-IconButton[data-size="medium"] .canon-IconButtonIcon {
width: 1.5rem;
height: 1.5rem;
}
+6 -6
View File
@@ -16,30 +16,30 @@
}
}
.canon-Link--variant-body {
.canon-Link[data-variant="body"] {
font-size: var(--canon-font-size-3);
line-height: 140%;
}
.canon-Link--variant-subtitle {
.canon-Link[data-variant="subtitle"] {
font-size: var(--canon-font-size-4);
line-height: 140%;
}
.canon-Link--variant-caption {
.canon-Link[data-variant="caption"] {
font-size: var(--canon-font-size-2);
line-height: 140%;
}
.canon-Link--variant-label {
.canon-Link[data-variant="label"] {
font-size: var(--canon-font-size-1);
line-height: 140%;
}
.canon-Link--weight-regular {
.canon-Link[data-weight="regular"] {
font-weight: var(--canon-font-weight-regular);
}
.canon-Link--weight-bold {
.canon-Link[data-weight="bold"] {
font-weight: var(--canon-font-weight-bold);
}
+24 -24
View File
@@ -5,14 +5,14 @@
display: flex;
}
.canon-Select--label {
.canon-SelectLabel {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-primary);
margin-bottom: var(--canon-space-1_5);
}
.canon-Select--description {
.canon-SelectDescription {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-secondary);
@@ -20,7 +20,7 @@
margin: 0;
}
.canon-Select--error {
.canon-SelectError {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-danger);
@@ -28,7 +28,7 @@
margin: 0;
}
.canon-Select--trigger {
.canon-SelectTrigger {
box-sizing: border-box;
border-radius: var(--canon-radius-3);
border: 1px solid var(--canon-border);
@@ -47,38 +47,38 @@
display: flex;
}
.canon-Select--trigger::placeholder {
.canon-SelectTrigger::placeholder {
color: var(--canon-fg-secondary);
}
.canon-Select--trigger:hover {
.canon-SelectTrigger:hover {
border-color: var(--canon-border-hover);
}
.canon-Select--trigger:focus-visible {
.canon-SelectTrigger:focus-visible {
border-color: var(--canon-border-pressed);
outline: 0;
}
.canon-Select--trigger[data-invalid] {
.canon-SelectTrigger[data-invalid] {
border-color: var(--canon-fg-danger);
}
.canon-Select--trigger[data-invalid]:hover, .canon-Select--trigger[data-invalid]:focus-visible {
.canon-SelectTrigger[data-invalid]:hover, .canon-SelectTrigger[data-invalid]:focus-visible {
border-width: 2px;
}
.canon-Select--trigger[data-disabled] {
.canon-SelectTrigger[data-disabled] {
cursor: not-allowed;
border-color: var(--canon-border-disabled);
color: var(--canon-fg-disabled);
}
.canon-Select--trigger-size-small {
.canon-SelectTrigger[data-size="small"] {
height: 2rem;
}
.canon-Select--trigger-size-medium {
.canon-SelectTrigger[data-size="medium"] {
height: 3rem;
}
@@ -87,11 +87,11 @@
transition: transform .2s;
}
.canon-Select--trigger[data-popup-open] .canon-SelectIcon {
.canon-SelectTrigger[data-popup-open] .canon-SelectIcon {
transform: rotate(180deg);
}
.canon-Select--popup {
.canon-SelectPopup {
box-sizing: border-box;
max-height: var(--available-height);
background-color: var(--canon-bg-surface-1);
@@ -106,12 +106,12 @@
box-shadow: 0 4px 12px #0003;
}
.canon-Select--popup[data-starting-style], .canon-Select--popup[data-ending-style] {
.canon-SelectPopup[data-starting-style], .canon-SelectPopup[data-ending-style] {
opacity: 0;
transform: scale(.9);
}
.canon-Select--item {
.canon-SelectItem {
width: var(--anchor-width);
padding-block: var(--canon-space-2);
padding-inline: var(--canon-space-4);
@@ -129,13 +129,13 @@
position: relative;
}
.canon-Select--item[data-highlighted] {
.canon-SelectItem[data-highlighted] {
z-index: 0;
color: var(--canon-fg-primary);
position: relative;
}
.canon-Select--item[data-highlighted]:before {
.canon-SelectItem[data-highlighted]:before {
content: "";
z-index: -1;
background-color: var(--canon-bg-tint-hover);
@@ -145,37 +145,37 @@
inset-inline: .25rem;
}
.canon-Select--item[data-disabled] {
.canon-SelectItem[data-disabled] {
cursor: not-allowed;
color: var(--canon-fg-disabled);
}
.canon-Select--item-indicator {
.canon-SelectItemIndicator {
grid-area: icon;
justify-content: center;
align-items: center;
display: flex;
}
.canon-Select--item-text {
.canon-SelectItemText {
flex: 1;
grid-area: text;
}
.canon-Select--required {
.canon-SelectRequired {
color: var(--canon-fg-secondary);
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
margin-left: var(--canon-space-1);
}
.canon-Select--icon {
.canon-SelectIcon {
justify-content: center;
align-items: center;
display: flex;
}
.canon-Select--value {
.canon-SelectValue {
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
+123 -116
View File
@@ -9246,7 +9246,7 @@
}
}
.canon-Button--variant-primary {
.canon-Button[data-variant="primary"] {
background-color: var(--canon-bg-solid);
color: var(--canon-fg-solid);
transition: background-color .15s, box-shadow .15s;
@@ -9270,7 +9270,7 @@
}
}
.canon-Button--variant-secondary {
.canon-Button[data-variant="secondary"] {
background-color: var(--canon-bg-surface-1);
box-shadow: inset 0 0 0 1px var(--canon-border);
color: var(--canon-fg-primary);
@@ -9296,28 +9296,58 @@
}
}
.canon-Button--size-medium {
.canon-Button[data-size="medium"] {
font-size: var(--canon-font-size-4);
padding: 0 var(--canon-space-3);
height: 40px;
}
.canon-Button--size-small {
.canon-Button[data-size="small"] {
font-size: var(--canon-font-size-3);
padding: 0 var(--canon-space-2);
height: 32px;
}
.canon-Button--size-small .canon-Button--icon {
.canon-Button[data-size="small"] .canon-ButtonIcon {
width: 1rem;
height: 1rem;
}
.canon-Button--size-medium .canon-Button--icon {
.canon-Button[data-size="medium"] .canon-ButtonIcon {
width: 1.5rem;
height: 1.5rem;
}
.canon-DataTableRoot {
gap: var(--canon-space-3);
flex-direction: column;
display: flex;
}
.canon-DataTablePagination {
padding-top: var(--canon-space-5);
justify-content: space-between;
align-items: center;
display: flex;
}
.canon-DataTablePagination--left {
justify-content: space-between;
align-items: center;
display: flex;
}
.canon-DataTablePagination--right {
justify-content: space-between;
align-items: center;
gap: var(--canon-space-2);
display: flex;
}
.canon-DataTablePagination--select {
min-width: 10.5rem;
}
.canon-Flex {
display: flex;
}
@@ -9403,117 +9433,94 @@
display: flex;
}
.table {
background-color: var(--canon-bg-surface-1);
border-radius: var(--canon-radius-2);
.canon-TableRoot {
caption-side: bottom;
border-collapse: collapse;
width: 100%;
padding-bottom: var(--canon-space-0_5);
padding-top: var(--canon-space-0_5);
font-size: var(--canon-font-size-3);
font-family: var(--canon-font-regular);
font-weight: var(--canon-font-weight-regular);
position: relative;
overflow: auto;
& table {
caption-side: bottom;
width: 100%;
font-size: var(--canon-font-size-sm);
border-collapse: collapse;
}
}
.table-head {
.canon-TableHead {
text-align: left;
padding: var(--canon-space-3);
font-size: var(--canon-font-size-3);
color: var(--canon-fg-primary);
}
.table-body {
& tr:last-child {
border-bottom: none;
}
.canon-TableBody {
color: var(--canon-fg-primary);
}
.table-row {
.canon-TableRow {
border-bottom: 1px solid var(--canon-border);
transition: color .2s ease-in-out;
&:hover td {
background-color: var(--canon-bg-tint);
}
& .table-cell:first-child {
border-top-left-radius: var(--canon-radius-2);
border-bottom-left-radius: var(--canon-radius-2);
box-shadow: inset 4px 2px 0 0 var(--canon-bg-surface-1), inset 4px -2px 0 0 var(--canon-bg-surface-1);
padding-left: var(--canon-space-3);
}
& .table-cell:last-child {
border-top-right-radius: var(--canon-radius-2);
border-bottom-right-radius: var(--canon-radius-2);
box-shadow: inset -4px 2px 0 0 var(--canon-bg-surface-1), inset -4px -2px 0 0 var(--canon-bg-surface-1);
padding-right: var(--canon-space-3);
}
}
.table-cell {
.canon-TableBody .canon-TableRow:hover {
background-color: var(--canon-gray-2);
}
.canon-TableCell {
padding: var(--canon-space-3);
background-color: var(--canon-bg);
box-shadow: inset 0px 2px 0 0 var(--canon-bg-surface-1), inset 0px -2px 0 0 var(--canon-bg-surface-1);
font-size: var(--canon-font-size-3);
}
.canon-TableCellText, .canon-TableCellLink {
gap: var(--canon-space-0_5);
flex-direction: column;
display: flex;
}
.canon-Text {
font-family: var(--canon-font-regular);
color: var(--canon-fg-primary);
margin: 0;
padding: 0;
}
.canon-Text--variant-body {
.canon-Text[data-variant="body"] {
font-size: var(--canon-font-size-3);
line-height: 140%;
}
.canon-Text--variant-subtitle {
.canon-Text[data-variant="subtitle"] {
font-size: var(--canon-font-size-4);
line-height: 140%;
}
.canon-Text--variant-caption {
.canon-Text[data-variant="caption"] {
font-size: var(--canon-font-size-2);
line-height: 140%;
}
.canon-Text--variant-label {
.canon-Text[data-variant="label"] {
font-size: var(--canon-font-size-1);
line-height: 140%;
}
.canon-Text--weight-regular {
.canon-Text[data-weight="regular"] {
font-weight: var(--canon-font-weight-regular);
}
.canon-Text--weight-bold {
.canon-Text[data-weight="bold"] {
font-weight: var(--canon-font-weight-bold);
}
.canon-Text--color-primary {
.canon-Text[data-color="primary"] {
color: var(--canon-fg-primary);
}
.canon-Text--color-secondary {
.canon-Text[data-color="secondary"] {
color: var(--canon-fg-secondary);
}
.canon-Text--color-danger {
.canon-Text[data-color="danger"] {
color: var(--canon-fg-danger);
}
.canon-Text--color-warning {
.canon-Text[data-color="warning"] {
color: var(--canon-fg-warning);
}
.canon-Text--color-success {
.canon-Text[data-color="success"] {
color: var(--canon-fg-success);
}
@@ -9525,32 +9532,32 @@
line-height: 100%;
}
.canon-Heading--variant-display {
.canon-Heading[data-variant="display"] {
font-size: var(--canon-font-size-10);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title1 {
.canon-Heading[data-variant="title1"] {
font-size: var(--canon-font-size-9);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title2 {
.canon-Heading[data-variant="title2"] {
font-size: var(--canon-font-size-8);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title3 {
.canon-Heading[data-variant="title3"] {
font-size: var(--canon-font-size-7);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title4 {
.canon-Heading[data-variant="title4"] {
font-size: var(--canon-font-size-title4);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title5 {
.canon-Heading[data-variant="title5"] {
font-size: var(--canon-font-size-5);
font-weight: var(--canon-font-weight-bold);
}
@@ -9573,7 +9580,7 @@
}
}
.canon-IconButton--variant-primary {
.canon-IconButton[data-variant="primary"] {
background-color: var(--canon-bg-solid);
color: var(--canon-fg-solid);
transition: background-color .15s, box-shadow .15s;
@@ -9597,7 +9604,7 @@
}
}
.canon-IconButton--variant-secondary {
.canon-IconButton[data-variant="secondary"] {
background-color: var(--canon-bg-surface-1);
box-shadow: inset 0 0 0 1px var(--canon-border);
color: var(--canon-fg-primary);
@@ -9623,24 +9630,24 @@
}
}
.canon-IconButton--size-medium {
.canon-IconButton[data-size="medium"] {
font-size: var(--canon-font-size-4);
width: 40px;
height: 40px;
}
.canon-IconButton--size-small {
.canon-IconButton[data-size="small"] {
font-size: var(--canon-font-size-3);
width: 32px;
height: 32px;
}
.canon-IconButton--size-small .canon-IconButton--icon {
.canon-IconButton[data-size="small"] .canon-IconButtonIcon {
width: 1rem;
height: 1rem;
}
.canon-IconButton--size-medium .canon-IconButton--icon {
.canon-IconButton[data-size="medium"] .canon-IconButtonIcon {
width: 1.5rem;
height: 1.5rem;
}
@@ -9652,14 +9659,14 @@
display: flex;
}
.canon-TextField--label {
.canon-TextFieldLabel {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-primary);
margin-bottom: var(--canon-space-1_5);
}
.canon-TextField--description {
.canon-TextFieldDescription {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-secondary);
@@ -9667,7 +9674,7 @@
margin: 0;
}
.canon-TextField--error {
.canon-TextFieldError {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-danger);
@@ -9675,7 +9682,7 @@
margin: 0;
}
.canon-TextField--input {
.canon-TextFieldInput {
border-radius: var(--canon-radius-3);
border: 1px solid var(--canon-border);
padding: 0 var(--canon-space-4);
@@ -9688,39 +9695,39 @@
transition: border-color .2s ease-in-out, outline-color .2s ease-in-out;
}
.canon-TextField--input::placeholder {
.canon-TextFieldInput::placeholder {
color: var(--canon-fg-secondary);
}
.canon-TextField--input:hover {
.canon-TextFieldInput:hover {
border-color: var(--canon-border-hover);
}
.canon-TextField--input:focus-visible {
.canon-TextFieldInput:focus-visible {
outline-color: var(--canon-border-pressed);
border-color: var(--canon-border-pressed);
outline-width: 0;
}
.canon-TextField--input[data-invalid] {
.canon-TextFieldInput[data-invalid] {
border-color: var(--canon-fg-danger);
}
.canon-TextField--input[data-disabled] {
.canon-TextFieldInput[data-disabled] {
opacity: .5;
cursor: not-allowed;
border: 1px solid var(--canon-border-disabled);
}
.canon-TextField--input-size-small {
.canon-TextFieldInput[data-size="small"] {
height: 2rem;
}
.canon-TextField--input-size-medium {
.canon-TextFieldInput[data-size="medium"] {
height: 2.5rem;
}
.canon-TextField--required {
.canon-TextFieldRequired {
color: var(--canon-fg-secondary);
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
@@ -9797,31 +9804,31 @@
}
}
.canon-Link--variant-body {
.canon-Link[data-variant="body"] {
font-size: var(--canon-font-size-3);
line-height: 140%;
}
.canon-Link--variant-subtitle {
.canon-Link[data-variant="subtitle"] {
font-size: var(--canon-font-size-4);
line-height: 140%;
}
.canon-Link--variant-caption {
.canon-Link[data-variant="caption"] {
font-size: var(--canon-font-size-2);
line-height: 140%;
}
.canon-Link--variant-label {
.canon-Link[data-variant="label"] {
font-size: var(--canon-font-size-1);
line-height: 140%;
}
.canon-Link--weight-regular {
.canon-Link[data-weight="regular"] {
font-weight: var(--canon-font-weight-regular);
}
.canon-Link--weight-bold {
.canon-Link[data-weight="bold"] {
font-weight: var(--canon-font-weight-bold);
}
@@ -9945,14 +9952,14 @@
display: flex;
}
.canon-Select--label {
.canon-SelectLabel {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-primary);
margin-bottom: var(--canon-space-1_5);
}
.canon-Select--description {
.canon-SelectDescription {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-secondary);
@@ -9960,7 +9967,7 @@
margin: 0;
}
.canon-Select--error {
.canon-SelectError {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-danger);
@@ -9968,7 +9975,7 @@
margin: 0;
}
.canon-Select--trigger {
.canon-SelectTrigger {
box-sizing: border-box;
border-radius: var(--canon-radius-3);
border: 1px solid var(--canon-border);
@@ -9987,38 +9994,38 @@
display: flex;
}
.canon-Select--trigger::placeholder {
.canon-SelectTrigger::placeholder {
color: var(--canon-fg-secondary);
}
.canon-Select--trigger:hover {
.canon-SelectTrigger:hover {
border-color: var(--canon-border-hover);
}
.canon-Select--trigger:focus-visible {
.canon-SelectTrigger:focus-visible {
border-color: var(--canon-border-pressed);
outline: 0;
}
.canon-Select--trigger[data-invalid] {
.canon-SelectTrigger[data-invalid] {
border-color: var(--canon-fg-danger);
}
.canon-Select--trigger[data-invalid]:hover, .canon-Select--trigger[data-invalid]:focus-visible {
.canon-SelectTrigger[data-invalid]:hover, .canon-SelectTrigger[data-invalid]:focus-visible {
border-width: 2px;
}
.canon-Select--trigger[data-disabled] {
.canon-SelectTrigger[data-disabled] {
cursor: not-allowed;
border-color: var(--canon-border-disabled);
color: var(--canon-fg-disabled);
}
.canon-Select--trigger-size-small {
.canon-SelectTrigger[data-size="small"] {
height: 2rem;
}
.canon-Select--trigger-size-medium {
.canon-SelectTrigger[data-size="medium"] {
height: 3rem;
}
@@ -10027,11 +10034,11 @@
transition: transform .2s;
}
.canon-Select--trigger[data-popup-open] .canon-SelectIcon {
.canon-SelectTrigger[data-popup-open] .canon-SelectIcon {
transform: rotate(180deg);
}
.canon-Select--popup {
.canon-SelectPopup {
box-sizing: border-box;
max-height: var(--available-height);
background-color: var(--canon-bg-surface-1);
@@ -10046,12 +10053,12 @@
box-shadow: 0 4px 12px #0003;
}
.canon-Select--popup[data-starting-style], .canon-Select--popup[data-ending-style] {
.canon-SelectPopup[data-starting-style], .canon-SelectPopup[data-ending-style] {
opacity: 0;
transform: scale(.9);
}
.canon-Select--item {
.canon-SelectItem {
width: var(--anchor-width);
padding-block: var(--canon-space-2);
padding-inline: var(--canon-space-4);
@@ -10069,13 +10076,13 @@
position: relative;
}
.canon-Select--item[data-highlighted] {
.canon-SelectItem[data-highlighted] {
z-index: 0;
color: var(--canon-fg-primary);
position: relative;
}
.canon-Select--item[data-highlighted]:before {
.canon-SelectItem[data-highlighted]:before {
content: "";
z-index: -1;
background-color: var(--canon-bg-tint-hover);
@@ -10085,37 +10092,37 @@
inset-inline: .25rem;
}
.canon-Select--item[data-disabled] {
.canon-SelectItem[data-disabled] {
cursor: not-allowed;
color: var(--canon-fg-disabled);
}
.canon-Select--item-indicator {
.canon-SelectItemIndicator {
grid-area: icon;
justify-content: center;
align-items: center;
display: flex;
}
.canon-Select--item-text {
.canon-SelectItemText {
flex: 1;
grid-area: text;
}
.canon-Select--required {
.canon-SelectRequired {
color: var(--canon-fg-secondary);
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
margin-left: var(--canon-space-1);
}
.canon-Select--icon {
.canon-SelectIcon {
justify-content: center;
align-items: center;
display: flex;
}
.canon-Select--value {
.canon-SelectValue {
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
+2 -56
View File
@@ -1,58 +1,4 @@
.table {
background-color: var(--canon-bg-surface-1);
border-radius: var(--canon-radius-2);
width: 100%;
padding-bottom: var(--canon-space-0_5);
padding-top: var(--canon-space-0_5);
.canon-TableCell {
padding: var(--canon-space-3);
font-size: var(--canon-font-size-3);
font-family: var(--canon-font-regular);
font-weight: var(--canon-font-weight-regular);
position: relative;
overflow: auto;
& table {
caption-side: bottom;
width: 100%;
font-size: var(--canon-font-size-sm);
border-collapse: collapse;
}
}
.table-head {
text-align: left;
padding: var(--canon-space-3);
}
.table-body {
& tr:last-child {
border-bottom: none;
}
}
.table-row {
transition: color .2s ease-in-out;
&:hover td {
background-color: var(--canon-bg-tint);
}
& .table-cell:first-child {
border-top-left-radius: var(--canon-radius-2);
border-bottom-left-radius: var(--canon-radius-2);
box-shadow: inset 4px 2px 0 0 var(--canon-bg-surface-1), inset 4px -2px 0 0 var(--canon-bg-surface-1);
padding-left: var(--canon-space-3);
}
& .table-cell:last-child {
border-top-right-radius: var(--canon-radius-2);
border-bottom-right-radius: var(--canon-radius-2);
box-shadow: inset -4px 2px 0 0 var(--canon-bg-surface-1), inset -4px -2px 0 0 var(--canon-bg-surface-1);
padding-right: var(--canon-space-3);
}
}
.table-cell {
padding: var(--canon-space-3);
background-color: var(--canon-bg);
box-shadow: inset 0px 2px 0 0 var(--canon-bg-surface-1), inset 0px -2px 0 0 var(--canon-bg-surface-1);
}
+11 -12
View File
@@ -1,54 +1,53 @@
.canon-Text {
font-family: var(--canon-font-regular);
color: var(--canon-fg-primary);
margin: 0;
padding: 0;
}
.canon-Text--variant-body {
.canon-Text[data-variant="body"] {
font-size: var(--canon-font-size-3);
line-height: 140%;
}
.canon-Text--variant-subtitle {
.canon-Text[data-variant="subtitle"] {
font-size: var(--canon-font-size-4);
line-height: 140%;
}
.canon-Text--variant-caption {
.canon-Text[data-variant="caption"] {
font-size: var(--canon-font-size-2);
line-height: 140%;
}
.canon-Text--variant-label {
.canon-Text[data-variant="label"] {
font-size: var(--canon-font-size-1);
line-height: 140%;
}
.canon-Text--weight-regular {
.canon-Text[data-weight="regular"] {
font-weight: var(--canon-font-weight-regular);
}
.canon-Text--weight-bold {
.canon-Text[data-weight="bold"] {
font-weight: var(--canon-font-weight-bold);
}
.canon-Text--color-primary {
.canon-Text[data-color="primary"] {
color: var(--canon-fg-primary);
}
.canon-Text--color-secondary {
.canon-Text[data-color="secondary"] {
color: var(--canon-fg-secondary);
}
.canon-Text--color-danger {
.canon-Text[data-color="danger"] {
color: var(--canon-fg-danger);
}
.canon-Text--color-warning {
.canon-Text[data-color="warning"] {
color: var(--canon-fg-warning);
}
.canon-Text--color-success {
.canon-Text[data-color="success"] {
color: var(--canon-fg-success);
}
+12 -12
View File
@@ -5,14 +5,14 @@
display: flex;
}
.canon-TextField--label {
.canon-TextFieldLabel {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-primary);
margin-bottom: var(--canon-space-1_5);
}
.canon-TextField--description {
.canon-TextFieldDescription {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-secondary);
@@ -20,7 +20,7 @@
margin: 0;
}
.canon-TextField--error {
.canon-TextFieldError {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-danger);
@@ -28,7 +28,7 @@
margin: 0;
}
.canon-TextField--input {
.canon-TextFieldInput {
border-radius: var(--canon-radius-3);
border: 1px solid var(--canon-border);
padding: 0 var(--canon-space-4);
@@ -41,39 +41,39 @@
transition: border-color .2s ease-in-out, outline-color .2s ease-in-out;
}
.canon-TextField--input::placeholder {
.canon-TextFieldInput::placeholder {
color: var(--canon-fg-secondary);
}
.canon-TextField--input:hover {
.canon-TextFieldInput:hover {
border-color: var(--canon-border-hover);
}
.canon-TextField--input:focus-visible {
.canon-TextFieldInput:focus-visible {
outline-color: var(--canon-border-pressed);
border-color: var(--canon-border-pressed);
outline-width: 0;
}
.canon-TextField--input[data-invalid] {
.canon-TextFieldInput[data-invalid] {
border-color: var(--canon-fg-danger);
}
.canon-TextField--input[data-disabled] {
.canon-TextFieldInput[data-disabled] {
opacity: .5;
cursor: not-allowed;
border: 1px solid var(--canon-border-disabled);
}
.canon-TextField--input-size-small {
.canon-TextFieldInput[data-size="small"] {
height: 2rem;
}
.canon-TextField--input-size-medium {
.canon-TextFieldInput[data-size="medium"] {
height: 2.5rem;
}
.canon-TextField--required {
.canon-TextFieldRequired {
color: var(--canon-fg-secondary);
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
+1
View File
@@ -56,6 +56,7 @@
"@storybook/react": "^8.6.8",
"@storybook/react-webpack5": "^8.6.8",
"@storybook/test": "^8.6.8",
"@tanstack/react-table": "^8.21.2",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"chalk": "^5.4.1",
+111 -33
View File
@@ -17,6 +17,7 @@ import { ReactNode } from 'react';
import { RefAttributes } from 'react';
import type { RemixiconComponentType } from '@remixicon/react';
import { ScrollArea as ScrollArea_2 } from '@base-ui-components/react/scroll-area';
import { Table as Table_2 } from '@tanstack/react-table';
import { TdHTMLAttributes } from 'react';
import { ThHTMLAttributes } from 'react';
import { Tooltip as Tooltip_2 } from '@base-ui-components/react/tooltip';
@@ -219,6 +220,65 @@ export interface ContainerProps {
style?: React.CSSProperties;
}
// @public
export const DataTable: {
Root: <TData>(
props: {
table: Table_2<TData>;
} & React.HTMLAttributes<HTMLDivElement>,
) => JSX.Element;
Pagination: ForwardRefExoticComponent<
DataTablePaginationProps & RefAttributes<HTMLDivElement>
>;
Table: ForwardRefExoticComponent<
DataTableTableProps & RefAttributes<HTMLTableElement>
>;
TableRoot: ForwardRefExoticComponent<
Omit<
HTMLAttributes<HTMLTableElement> & RefAttributes<HTMLTableElement>,
'ref'
> &
RefAttributes<HTMLTableElement>
>;
TableHeader: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableSectionElement> &
RefAttributes<HTMLTableSectionElement>
>;
TableBody: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableSectionElement> &
RefAttributes<HTMLTableSectionElement>
>;
TableRow: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableRowElement> & RefAttributes<HTMLTableRowElement>
>;
TableCell: ForwardRefExoticComponent<
TdHTMLAttributes<HTMLTableCellElement> & RefAttributes<HTMLTableCellElement>
>;
TableCellText: ForwardRefExoticComponent<
TableCellTextProps & RefAttributes<HTMLDivElement>
>;
TableCellLink: ForwardRefExoticComponent<
TableCellLinkProps & RefAttributes<HTMLDivElement>
>;
TableHead: ForwardRefExoticComponent<
ThHTMLAttributes<HTMLTableCellElement> & RefAttributes<HTMLTableCellElement>
>;
};
// @public (undocumented)
export interface DataTablePaginationProps
extends React.HTMLAttributes<HTMLDivElement> {}
// @public (undocumented)
export interface DataTableRootProps<TData>
extends React.HTMLAttributes<HTMLDivElement> {
table: Table_2<TData>;
}
// @public (undocumented)
export interface DataTableTableProps
extends React.HTMLAttributes<HTMLTableElement> {}
// @public (undocumented)
export type Display = 'none' | 'flex' | 'block' | 'inline';
@@ -1028,43 +1088,61 @@ export type StylingPropDef = {
parseValue?: (value: string) => string | undefined;
};
// @public (undocumented)
export const Table: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableElement> & RefAttributes<HTMLTableElement>
>;
// @public
export const Table: {
Root: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableElement> & RefAttributes<HTMLTableElement>
>;
Header: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableSectionElement> &
RefAttributes<HTMLTableSectionElement>
>;
Body: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableSectionElement> &
RefAttributes<HTMLTableSectionElement>
>;
Head: ForwardRefExoticComponent<
ThHTMLAttributes<HTMLTableCellElement> & RefAttributes<HTMLTableCellElement>
>;
Row: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableRowElement> & RefAttributes<HTMLTableRowElement>
>;
Cell: ForwardRefExoticComponent<
TdHTMLAttributes<HTMLTableCellElement> & RefAttributes<HTMLTableCellElement>
>;
CellText: ForwardRefExoticComponent<
TableCellTextProps & RefAttributes<HTMLDivElement>
>;
CellLink: ForwardRefExoticComponent<
TableCellLinkProps & RefAttributes<HTMLDivElement>
>;
Caption: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableCaptionElement> &
RefAttributes<HTMLTableCaptionElement>
>;
};
// @public (undocumented)
export const TableBody: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableSectionElement> &
RefAttributes<HTMLTableSectionElement>
>;
export interface TableCellLinkProps
extends React.TdHTMLAttributes<HTMLTableCellElement> {
// (undocumented)
description?: string;
// (undocumented)
href: string;
// (undocumented)
render?: useRender.ComponentProps<'a'>['render'];
// (undocumented)
title: string;
}
// @public (undocumented)
export const TableCell: ForwardRefExoticComponent<
TdHTMLAttributes<HTMLTableCellElement> & RefAttributes<HTMLTableCellElement>
>;
// @public (undocumented)
export const TableFooter: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableSectionElement> &
RefAttributes<HTMLTableSectionElement>
>;
// @public (undocumented)
export const TableHead: ForwardRefExoticComponent<
ThHTMLAttributes<HTMLTableCellElement> & RefAttributes<HTMLTableCellElement>
>;
// @public (undocumented)
export const TableHeader: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableSectionElement> &
RefAttributes<HTMLTableSectionElement>
>;
// @public (undocumented)
export const TableRow: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableRowElement> & RefAttributes<HTMLTableRowElement>
>;
export interface TableCellTextProps
extends React.TdHTMLAttributes<HTMLTableCellElement> {
// (undocumented)
description?: string;
// (undocumented)
title: string;
}
// @public (undocumented)
const Text_2: ForwardRefExoticComponent<
@@ -44,18 +44,15 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
<button
ref={ref}
disabled={disabled}
className={clsx(
'canon-Button',
`canon-Button--size-${responsiveSize}`,
`canon-Button--variant-${responsiveVariant}`,
className,
)}
className={clsx('canon-Button', className)}
data-size={responsiveSize}
data-variant={responsiveVariant}
style={style}
{...rest}
>
{iconStart && <Icon name={iconStart} className="canon-Button--icon" />}
{iconStart && <Icon name={iconStart} className="canon-ButtonIcon" />}
{children}
{iconEnd && <Icon name={iconEnd} className="canon-Button--icon" />}
{iconEnd && <Icon name={iconEnd} className="canon-ButtonIcon" />}
</button>
);
},
@@ -32,7 +32,7 @@
}
}
.canon-Button--variant-primary {
.canon-Button[data-variant='primary'] {
background-color: var(--canon-bg-solid);
color: var(--canon-fg-solid);
transition: background-color 150ms ease, box-shadow 150ms ease;
@@ -56,7 +56,7 @@
}
}
.canon-Button--variant-secondary {
.canon-Button[data-variant='secondary'] {
background-color: var(--canon-bg-surface-1);
box-shadow: inset 0 0 0 1px var(--canon-border);
color: var(--canon-fg-primary);
@@ -82,24 +82,24 @@
}
}
.canon-Button--size-medium {
.canon-Button[data-size='medium'] {
font-size: var(--canon-font-size-4);
padding: 0 var(--canon-space-3);
height: 40px;
}
.canon-Button--size-small {
.canon-Button[data-size='small'] {
font-size: var(--canon-font-size-3);
padding: 0 var(--canon-space-2);
height: 32px;
}
.canon-Button--size-small .canon-Button--icon {
.canon-Button[data-size='small'] .canon-ButtonIcon {
width: 1rem;
height: 1rem;
}
.canon-Button--size-medium .canon-Button--icon {
.canon-Button[data-size='medium'] .canon-ButtonIcon {
width: 1.5rem;
height: 1.5rem;
}
@@ -0,0 +1,145 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react';
import { DataTable } from '.';
import { data, DataProps } from './mocked-components';
import { columns } from './mocked-columns';
import {
flexRender,
getCoreRowModel,
getPaginationRowModel,
useReactTable,
PaginationState,
} from '@tanstack/react-table';
import { useState } from 'react';
const meta = {
title: 'Components/DataTable',
} satisfies Meta;
export default meta;
type Story = StoryObj<typeof meta>;
export const Uncontrolled: Story = {
render: () => {
const table = useReactTable<DataProps>({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
});
return (
<DataTable.Root table={table}>
<DataTable.Table />
<DataTable.Pagination />
</DataTable.Root>
);
},
};
export const Controlled: Story = {
render: () => {
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 4,
pageSize: 5,
});
const table = useReactTable<DataProps>({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
state: {
pagination,
},
onPaginationChange: setPagination,
});
return (
<DataTable.Root table={table}>
<DataTable.Table />
<DataTable.Pagination />
</DataTable.Root>
);
},
};
export const WithCustomTable: Story = {
render: () => {
const table = useReactTable<DataProps>({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
});
return (
<DataTable.Root table={table}>
<DataTable.TableRoot>
<DataTable.TableHeader>
{table.getHeaderGroups().map(headerGroup => (
<DataTable.TableRow key={headerGroup.id}>
{headerGroup.headers.map(header => {
return (
<DataTable.TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</DataTable.TableHead>
);
})}
</DataTable.TableRow>
))}
</DataTable.TableHeader>
<DataTable.TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map(row => (
<DataTable.TableRow
key={row.id}
data-state={row.getIsSelected() && 'selected'}
>
{row.getVisibleCells().map(cell => (
<DataTable.TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</DataTable.TableCell>
))}
</DataTable.TableRow>
))
) : (
<DataTable.TableRow>
<DataTable.TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</DataTable.TableCell>
</DataTable.TableRow>
)}
</DataTable.TableBody>
</DataTable.TableRoot>
<DataTable.Pagination />
</DataTable.Root>
);
},
};
@@ -0,0 +1,50 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { forwardRef } from 'react';
import { Table } from '../Table';
import { DataTableRoot } from './Root/DataTableRoot';
import { DataTablePagination } from './Pagination/DataTablePagination';
import { Table as TanstackTable } from '@tanstack/react-table';
import { DataTableTable } from './Table/DataTableTable';
const TableRoot = forwardRef<
React.ElementRef<typeof Table.Root>,
React.ComponentPropsWithoutRef<typeof Table.Root>
>(({ className, ...props }, ref) => <Table.Root ref={ref} {...props} />);
TableRoot.displayName = Table.Root.displayName;
/**
* DataTable component for displaying tabular data with pagination
* @public
*/
export const DataTable = {
Root: DataTableRoot as <TData>(
props: {
table: TanstackTable<TData>;
} & React.HTMLAttributes<HTMLDivElement>,
) => JSX.Element,
Pagination: DataTablePagination,
Table: DataTableTable,
TableRoot: TableRoot,
TableHeader: Table.Header,
TableBody: Table.Body,
TableRow: Table.Row,
TableCell: Table.Cell,
TableCellText: Table.CellText,
TableCellLink: Table.CellLink,
TableHead: Table.Head,
};
@@ -0,0 +1,55 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react';
import { DataTablePagination } from './DataTablePagination';
import {
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from '@tanstack/react-table';
import { data, DataProps } from '../mocked-components';
import { columns } from '../mocked-columns';
import { DataTable } from '../DataTable';
const meta = {
title: 'Components/DataTable/Pagination',
component: DataTablePagination,
} satisfies Meta<typeof DataTablePagination>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
render: () => {
const table = useReactTable<DataProps>({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
});
return (
<DataTable.Root table={table}>
<DataTable.Pagination />
</DataTable.Root>
);
},
};
@@ -0,0 +1,23 @@
.canon-DataTablePagination {
display: flex;
align-items: center;
justify-content: space-between;
padding-top: var(--canon-space-5);
}
.canon-DataTablePagination--left {
display: flex;
align-items: center;
justify-content: space-between;
}
.canon-DataTablePagination--right {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--canon-space-2);
}
.canon-DataTablePagination--select {
min-width: 10.5rem;
}
@@ -0,0 +1,90 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { forwardRef } from 'react';
import { Text } from '../../Text';
import { DataTablePaginationProps } from './types';
import { IconButton } from '../../IconButton';
import clsx from 'clsx';
import { Select } from '../../Select';
import { useDataTable } from '../Root/DataTableRoot';
/** @public */
const DataTablePagination = forwardRef(
(
props: DataTablePaginationProps,
ref: React.ForwardedRef<HTMLDivElement>,
) => {
const { className, ...rest } = props;
const { table } = useDataTable();
const pageIndex = table?.getState().pagination.pageIndex;
const pageSize = table?.getState().pagination.pageSize;
return (
<div
ref={ref}
className={clsx('canon-DataTablePagination', className)}
{...rest}
>
<div className="canon-DataTablePagination--left">
{!table.options.manualPagination && (
<Select
name="pageSize"
size="small"
placeholder="Show 10 results"
options={[
{ label: 'Show 5 results', value: '5' },
{ label: 'Show 10 results', value: '10' },
{ label: 'Show 20 results', value: '20' },
{ label: 'Show 30 results', value: '30' },
{ label: 'Show 40 results', value: '40' },
{ label: 'Show 50 results', value: '50' },
]}
value={pageSize?.toString()}
onValueChange={value => {
table?.setPageSize(Number(value));
}}
className="canon-DataTablePagination--select"
/>
)}
</div>
<div className="canon-DataTablePagination--right">
<Text variant="body">{`${(pageIndex ?? 0) * (pageSize ?? 10) + 1} - ${
((pageIndex ?? 0) + 1) * (pageSize ?? 10)
} of ${table?.getRowCount()}`}</Text>
<IconButton
variant="secondary"
size="small"
onClick={() => table?.previousPage()}
disabled={!table?.getCanPreviousPage()}
icon="chevron-left"
/>
<IconButton
variant="secondary"
size="small"
onClick={() => table?.nextPage()}
disabled={!table?.getCanNextPage()}
icon="chevron-right"
/>
</div>
</div>
);
},
);
DataTablePagination.displayName = 'DataTablePagination';
export { DataTablePagination };
@@ -0,0 +1,19 @@
/*
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/** @public */
export interface DataTablePaginationProps
extends React.HTMLAttributes<HTMLDivElement> {}
@@ -0,0 +1,5 @@
.canon-DataTableRoot {
display: flex;
flex-direction: column;
gap: var(--canon-space-3);
}
@@ -0,0 +1,62 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { forwardRef, createContext, useContext } from 'react';
import clsx from 'clsx';
import { DataTableRootProps } from './types';
import { Table } from '@tanstack/react-table';
type DataTableContextType<TData> = {
table: Table<TData>;
};
/** @public */
export const DataTableContext = createContext<DataTableContextType<any> | null>(
null,
);
/** @public */
const DataTableRoot = forwardRef(
<TData,>(
props: DataTableRootProps<TData>,
ref: React.ForwardedRef<HTMLDivElement>,
) => {
const { className, table, ...rest } = props;
return (
<DataTableContext.Provider value={{ table }}>
<div
ref={ref}
className={clsx('canon-DataTable', className)}
{...rest}
/>
</DataTableContext.Provider>
);
},
);
DataTableRoot.displayName = 'DataTableRoot';
export { DataTableRoot };
/** @public */
export function useDataTable<TData>() {
const context = useContext(DataTableContext);
if (!context) {
throw new Error('useDataTable must be used within a DataTableRoot');
}
return context as DataTableContextType<TData>;
}
@@ -0,0 +1,26 @@
/*
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Table } from '@tanstack/react-table';
/** @public */
export interface DataTableRootProps<TData>
extends React.HTMLAttributes<HTMLDivElement> {
/**
* The table instance.
*/
table: Table<TData>;
}
@@ -0,0 +1,82 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { forwardRef } from 'react';
import clsx from 'clsx';
import { DataTableTableProps } from './types';
import { Table } from '../../Table';
import { useDataTable } from '../Root/DataTableRoot';
import { flexRender } from '@tanstack/react-table';
/** @public */
const DataTableTable = forwardRef(
(props: DataTableTableProps, ref: React.ForwardedRef<HTMLTableElement>) => {
const { className, ...rest } = props;
const { table } = useDataTable();
return (
<Table.Root ref={ref} className={clsx(className)} {...rest}>
<Table.Header>
{table.getHeaderGroups().map(headerGroup => (
<Table.Row key={headerGroup.id}>
{headerGroup.headers.map(header => {
return (
<Table.Head key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</Table.Head>
);
})}
</Table.Row>
))}
</Table.Header>
<Table.Body>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map(row => (
<Table.Row
key={row.id}
data-state={row.getIsSelected() && 'selected'}
>
{row.getVisibleCells().map(cell => (
<Table.Cell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</Table.Cell>
))}
</Table.Row>
))
) : (
<Table.Row>
<Table.Cell
colSpan={table.getAllColumns().length}
className="h-24 text-center"
>
No results.
</Table.Cell>
</Table.Row>
)}
</Table.Body>
</Table.Root>
);
},
);
DataTableTable.displayName = 'DataTableRoot';
export { DataTableTable };
@@ -0,0 +1,19 @@
/*
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/** @public */
export interface DataTableTableProps
extends React.HTMLAttributes<HTMLTableElement> {}
@@ -0,0 +1,20 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './DataTable';
export * from './Root/types';
export * from './Pagination/types';
export * from './Table/types';
@@ -0,0 +1,74 @@
/*
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ColumnDef } from '@tanstack/react-table';
import { DataProps } from './mocked-components';
import { Checkbox } from '../Checkbox';
import { DataTable } from './DataTable';
export const columns: ColumnDef<DataProps>[] = [
{
id: 'select',
header: ({ table }) => (
<Checkbox
checked={table.getIsAllPageRowsSelected()}
onChange={(checked: boolean) =>
table.toggleAllPageRowsSelected(checked)
}
aria-label="Select all"
/>
),
cell: ({ row }) => (
<td>
<Checkbox
checked={row.getIsSelected()}
onChange={(checked: boolean) => row.toggleSelected(checked)}
aria-label="Select row"
/>
</td>
),
enableSorting: false,
enableHiding: false,
},
{
accessorKey: 'name',
header: 'Name',
cell: ({ row }) => (
<DataTable.TableCellLink
title={row.getValue('name')}
description={row.original.description}
href="/"
/>
),
},
{
accessorKey: 'owner',
header: 'Owner',
cell: ({ row }) => (
<DataTable.TableCellText title={row.getValue('owner')} />
),
},
{
accessorKey: 'type',
header: 'Type',
cell: ({ row }) => <DataTable.TableCellText title={row.getValue('type')} />,
},
{
accessorKey: 'tags',
header: 'Tags',
cell: ({ row }) => <DataTable.TableCellText title={row.getValue('tags')} />,
},
];
@@ -0,0 +1,818 @@
/*
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export interface DataProps {
name: string;
owner: string;
type: 'documentation' | 'library' | 'service' | 'website' | 'other';
description?: string;
tags?: string[];
}
export const data: DataProps[] = [
{
name: 'authentication-and-authorization-service',
owner: 'security-team',
type: 'service',
description:
'A comprehensive service handling user authentication and role-based access control across all applications.',
tags: ['security', 'authentication', 'authorization'],
},
{
name: 'user-interface-dashboard-and-analytics-platform',
owner: 'frontend-team',
type: 'website',
description:
'Interactive dashboard providing real-time analytics and data visualization for business metrics.',
tags: ['analytics', 'visualization', 'dashboard'],
},
{
name: 'payment-gateway',
owner: 'finance-team',
type: 'service',
description:
'Secure payment processing system supporting multiple payment methods and currencies.',
tags: ['payments', 'security', 'finance'],
},
{
name: 'real-time-analytics-processing-and-visualization-engine',
owner: 'data-team',
type: 'service',
description:
'High-performance engine for processing and visualizing streaming data analytics.',
tags: ['analytics', 'real-time', 'data-processing'],
},
{
name: 'notification-center',
owner: 'platform-team',
type: 'service',
description:
'Centralized system for managing and delivering notifications across multiple channels.',
tags: ['notifications', 'messaging'],
},
{
name: 'administrative-control-panel-and-user-management-interface',
owner: 'frontend-team',
type: 'website',
description:
'Admin interface for managing users, permissions, and system configurations.',
tags: ['admin', 'user-management', 'configuration'],
},
{
name: 'search-indexer',
owner: 'search-team',
type: 'service',
description:
'Service responsible for indexing and updating searchable content across the platform.',
tags: ['search', 'indexing'],
},
{
name: 'cross-platform-mobile-application-framework',
owner: 'mobile-team',
type: 'website',
description:
'Framework enabling development of cross-platform mobile applications with shared codebase.',
tags: ['mobile', 'framework', 'cross-platform'],
},
{
name: 'database-migration',
owner: 'devops-team',
type: 'other',
description:
'Tools and scripts for managing database schema migrations and data transformations.',
tags: ['database', 'migration', 'devops'],
},
{
name: 'api-gateway',
owner: 'platform-team',
type: 'service',
description:
'Central entry point for all API requests, handling routing, authentication, and rate limiting.',
tags: ['api', 'gateway', 'security', 'routing'],
},
{
name: 'content-management',
owner: 'content-team',
type: 'service',
description:
'System for managing and delivering digital content across multiple channels.',
tags: ['content', 'management', 'delivery'],
},
{
name: 'enterprise-reporting-and-analytics-dashboard',
owner: 'analytics-team',
type: 'website',
description:
'Comprehensive business intelligence platform for enterprise-wide reporting and analytics.',
tags: ['analytics', 'reporting', 'business-intelligence'],
},
{
name: 'image-processing-and-optimization-service',
owner: 'media-team',
type: 'service',
description:
'Service for processing, optimizing, and delivering images across different devices and networks.',
tags: ['media', 'optimization', 'processing'],
},
{
name: 'customer-portal',
owner: 'frontend-team',
type: 'website',
description:
'Self-service portal for customers to manage their accounts and access services.',
tags: ['customer', 'self-service'],
},
{
name: 'log-aggregator',
owner: 'devops-team',
type: 'service',
description:
'Centralized logging system for collecting, processing, and analyzing application logs.',
tags: ['logging', 'monitoring', 'devops'],
},
{
name: 'identity-provider',
owner: 'security-team',
type: 'service',
description:
'Service managing user identities and authentication across the organization.',
tags: ['identity', 'security', 'authentication'],
},
{
name: 'document-storage',
owner: 'storage-team',
type: 'service',
description:
'Secure and scalable document storage system with version control and access management.',
tags: ['storage', 'documents', 'version-control'],
},
{
name: 'workflow-engine',
owner: 'platform-team',
type: 'service',
description:
'Engine for defining and executing business processes and workflows.',
tags: ['workflow', 'automation'],
},
{
name: 'mobile-backend',
owner: 'mobile-team',
type: 'service',
description:
'Backend services supporting mobile applications with optimized APIs and data synchronization.',
tags: ['mobile', 'backend', 'api'],
},
{
name: 'system-monitoring-and-alerting-dashboard',
owner: 'devops-team',
type: 'website',
description:
'Real-time monitoring and alerting system for infrastructure and application health.',
tags: ['monitoring', 'alerting', 'devops', 'infrastructure'],
},
{
name: 'email-service',
owner: 'communication-team',
type: 'service',
description:
'Reliable email delivery service with templates and tracking capabilities.',
tags: ['email', 'communication'],
},
{
name: 'data-pipeline',
owner: 'data-team',
type: 'service',
description:
'ETL pipeline for processing and transforming large volumes of data.',
tags: ['data', 'etl', 'pipeline'],
},
{
name: 'configuration-manager',
owner: 'platform-team',
type: 'service',
description:
'Centralized system for managing application configurations across environments.',
tags: ['configuration', 'management'],
},
{
name: 'testing-framework',
owner: 'qa-team',
type: 'library',
description:
'Comprehensive testing framework supporting various types of automated tests.',
tags: ['testing', 'automation', 'qa'],
},
{
name: 'cache-service',
owner: 'platform-team',
type: 'service',
description:
'Distributed caching service for improving application performance.',
tags: ['caching', 'performance'],
},
{
name: 'billing-system',
owner: 'finance-team',
type: 'service',
description:
'System for managing customer billing, invoicing, and payment processing.',
tags: ['billing', 'finance', 'payments'],
},
{
name: 'comprehensive-product-documentation-and-api-reference',
owner: 'docs-team',
type: 'documentation',
description:
'Complete documentation covering product features, APIs, and integration guides.',
tags: ['documentation', 'api', 'reference'],
},
{
name: 'queue-manager',
owner: 'platform-team',
type: 'service',
description:
'Message queue system for asynchronous processing and event handling.',
tags: ['queue', 'messaging', 'async'],
},
{
name: 'security-scanner',
owner: 'security-team',
type: 'other',
description:
'Automated security scanning tool for identifying vulnerabilities in code and infrastructure.',
tags: ['security', 'scanning', 'vulnerability'],
},
{
name: 'user-profile',
owner: 'frontend-team',
type: 'website',
description:
'User profile management interface with personalization features.',
tags: ['user', 'profile', 'personalization'],
},
{
name: 'data-warehouse',
owner: 'data-team',
type: 'service',
description:
'Centralized data repository for business intelligence and analytics.',
tags: ['data', 'warehouse', 'analytics'],
},
{
name: 'deployment-automation',
owner: 'devops-team',
type: 'other',
description:
'Automated deployment pipeline for continuous integration and delivery.',
tags: ['deployment', 'automation', 'ci-cd', 'devops'],
},
{
name: 'chat-service',
owner: 'communication-team',
type: 'service',
description:
'Real-time chat service supporting text, file sharing, and group conversations.',
tags: ['chat', 'communication', 'real-time'],
},
{
name: 'analytics-dashboard',
owner: 'analytics-team',
type: 'website',
description:
'Interactive dashboard for visualizing and analyzing business metrics.',
tags: ['analytics', 'dashboard', 'visualization'],
},
{
name: 'file-uploader',
owner: 'storage-team',
type: 'service',
description:
'Service for handling secure file uploads with progress tracking and validation.',
tags: ['storage', 'upload', 'files'],
},
{
name: 'search-service',
owner: 'search-team',
type: 'service',
description:
'Full-text search service with advanced filtering and ranking capabilities.',
tags: ['search', 'full-text'],
},
{
name: 'mobile-sdk',
owner: 'mobile-team',
type: 'library',
description:
'Software development kit for building mobile applications with native features.',
tags: ['mobile', 'sdk', 'development'],
},
{
name: 'performance-monitor',
owner: 'devops-team',
type: 'service',
description:
'System for monitoring and analyzing application performance metrics.',
tags: ['performance', 'monitoring', 'metrics'],
},
{
name: 'content-delivery',
owner: 'media-team',
type: 'service',
description:
'CDN service for optimized content delivery across global networks.',
tags: ['cdn', 'content', 'delivery'],
},
{
name: 'user-authentication',
owner: 'security-team',
type: 'service',
description:
'Service handling user login, session management, and authentication flows.',
tags: ['authentication', 'security', 'user'],
},
{
name: 'data-export',
owner: 'data-team',
type: 'service',
description:
'Service for exporting data in various formats with scheduling capabilities.',
tags: ['data', 'export', 'scheduling'],
},
{
name: 'admin-api',
owner: 'platform-team',
type: 'service',
description:
'API endpoints for administrative functions and system management.',
tags: ['api', 'admin', 'management'],
},
{
name: 'testing-dashboard',
owner: 'qa-team',
type: 'website',
description: 'Dashboard for monitoring test results and quality metrics.',
tags: ['testing', 'dashboard', 'qa'],
},
{
name: 'message-broker',
owner: 'platform-team',
type: 'service',
description:
'Message broker service for reliable event-driven communication between services.',
tags: ['messaging', 'broker', 'event-driven'],
},
{
name: 'payment-processor',
owner: 'finance-team',
type: 'service',
description:
'Service for processing financial transactions and payment methods.',
tags: ['payments', 'finance', 'processing'],
},
{
name: 'document-viewer',
owner: 'frontend-team',
type: 'website',
description: 'Web-based document viewer supporting multiple file formats.',
tags: ['documents', 'viewer'],
},
{
name: 'load-balancer',
owner: 'devops-team',
type: 'service',
description:
'Service for distributing network traffic across multiple servers.',
tags: ['load-balancing', 'networking', 'infrastructure'],
},
{
name: 'security-audit',
owner: 'security-team',
type: 'other',
description:
'Tools and processes for conducting security audits and compliance checks.',
tags: ['security', 'audit', 'compliance'],
},
{
name: 'user-settings',
owner: 'frontend-team',
type: 'website',
description:
'Interface for users to manage their preferences and account settings.',
tags: ['user', 'settings', 'preferences'],
},
{
name: 'data-import',
owner: 'data-team',
type: 'service',
description:
'Service for importing and validating data from external sources.',
tags: ['data', 'import', 'validation'],
},
{
name: 'infrastructure-monitor',
owner: 'devops-team',
type: 'service',
description:
'Monitoring system for infrastructure components and resources.',
tags: ['monitoring', 'infrastructure', 'devops'],
},
{
name: 'notification-manager',
owner: 'communication-team',
type: 'service',
description:
'Service for managing and delivering notifications across multiple channels.',
tags: ['notifications', 'management'],
},
{
name: 'analytics-processor',
owner: 'analytics-team',
type: 'service',
description:
'Service for processing and analyzing business data and metrics.',
tags: ['analytics', 'processing', 'metrics'],
},
{
name: 'file-manager',
owner: 'storage-team',
type: 'website',
description: 'Web interface for managing files and storage resources.',
tags: ['files', 'storage', 'management'],
},
{
name: 'search-index',
owner: 'search-team',
type: 'service',
description: 'Service for maintaining and updating search indices.',
tags: ['search', 'indexing'],
},
{
name: 'mobile-authentication',
owner: 'mobile-team',
type: 'service',
description:
'Authentication service specifically designed for mobile applications.',
tags: ['mobile', 'authentication', 'security'],
},
{
name: 'system-monitor',
owner: 'devops-team',
type: 'service',
description:
'Monitoring service for system health and performance metrics.',
tags: ['monitoring', 'system', 'metrics'],
},
{
name: 'media-processor',
owner: 'media-team',
type: 'service',
description: 'Service for processing and optimizing media files.',
tags: ['media', 'processing', 'optimization'],
},
{
name: 'user-management',
owner: 'security-team',
type: 'service',
description: 'Service for managing user accounts and permissions.',
tags: ['user', 'management', 'security'],
},
{
name: 'data-transformer',
owner: 'data-team',
type: 'service',
description:
'Service for transforming data between different formats and structures.',
tags: ['data', 'transformation'],
},
{
name: 'admin-dashboard',
owner: 'platform-team',
type: 'website',
description:
'Administrative interface for system management and monitoring.',
tags: ['admin', 'dashboard', 'management'],
},
{
name: 'test-automation',
owner: 'qa-team',
type: 'other',
description: 'Tools and frameworks for automating testing processes.',
tags: ['testing', 'automation', 'qa'],
},
{
name: 'event-bus',
owner: 'platform-team',
type: 'service',
description: 'Event-driven communication system between services.',
tags: ['events', 'messaging', 'communication'],
},
{
name: 'invoice-generator',
owner: 'finance-team',
type: 'service',
description: 'Service for generating and managing invoices.',
tags: ['invoices', 'finance'],
},
{
name: 'document-editor',
owner: 'frontend-team',
type: 'website',
description: 'Web-based document editing interface.',
tags: ['documents', 'editor'],
},
{
name: 'service-discovery',
owner: 'devops-team',
type: 'service',
description: 'Service for discovering and registering available services.',
tags: ['discovery', 'services', 'devops'],
},
{
name: 'security-monitor',
owner: 'security-team',
type: 'service',
description: 'Service for monitoring security events and threats.',
tags: ['security', 'monitoring', 'threats'],
},
{
name: 'user-preferences',
owner: 'frontend-team',
type: 'website',
description: 'Interface for managing user preferences and settings.',
tags: ['user', 'preferences'],
},
{
name: 'data-validator',
owner: 'data-team',
type: 'service',
description: 'Service for validating data integrity and format.',
tags: ['data', 'validation'],
},
{
name: 'infrastructure-automation',
owner: 'devops-team',
type: 'other',
description:
'Tools for automating infrastructure provisioning and management.',
tags: ['infrastructure', 'automation', 'devops'],
},
{
name: 'notification-dispatcher',
owner: 'communication-team',
type: 'service',
description:
'Service for dispatching notifications to appropriate channels.',
tags: ['notifications', 'dispatch'],
},
{
name: 'analytics-collector',
owner: 'analytics-team',
type: 'service',
description: 'Service for collecting and aggregating analytics data.',
tags: ['analytics', 'collection', 'aggregation'],
},
{
name: 'file-processor',
owner: 'storage-team',
type: 'service',
description: 'Service for processing and managing files.',
tags: ['files', 'processing'],
},
{
name: 'search-analyzer',
owner: 'search-team',
type: 'service',
description: 'Service for analyzing search queries and results.',
tags: ['search', 'analysis'],
},
{
name: 'mobile-notifications',
owner: 'mobile-team',
type: 'service',
description: 'Service for sending notifications to mobile devices.',
tags: ['mobile', 'notifications'],
},
{
name: 'system-alerts',
owner: 'devops-team',
type: 'service',
description: 'Service for managing and dispatching system alerts.',
tags: ['alerts', 'system', 'monitoring'],
},
{
name: 'media-encoder',
owner: 'media-team',
type: 'service',
description: 'Service for encoding and processing media files.',
tags: ['media', 'encoding'],
},
{
name: 'user-authorization',
owner: 'security-team',
type: 'service',
description: 'Service for managing user permissions and access control.',
tags: ['authorization', 'security', 'user'],
},
{
name: 'data-aggregator',
owner: 'data-team',
type: 'service',
description: 'Service for aggregating data from multiple sources.',
tags: ['data', 'aggregation'],
},
{
name: 'admin-authentication',
owner: 'platform-team',
type: 'service',
description: 'Authentication service for administrative access.',
tags: ['admin', 'authentication', 'security'],
},
{
name: 'test-coverage',
owner: 'qa-team',
type: 'other',
description: 'Tools for measuring and reporting test coverage.',
tags: ['testing', 'coverage', 'qa'],
},
{
name: 'event-processor',
owner: 'platform-team',
type: 'service',
description: 'Service for processing and handling events.',
tags: ['events', 'processing'],
},
{
name: 'payment-validator',
owner: 'finance-team',
type: 'service',
description: 'Service for validating payment transactions.',
tags: ['payments', 'validation', 'finance'],
},
{
name: 'document-converter',
owner: 'frontend-team',
type: 'service',
description: 'Service for converting documents between different formats.',
tags: ['documents', 'conversion'],
},
{
name: 'service-health',
owner: 'devops-team',
type: 'service',
description: 'Service for monitoring and reporting service health status.',
tags: ['health', 'monitoring', 'services'],
},
{
name: 'security-logger',
owner: 'security-team',
type: 'service',
description: 'Service for logging security-related events and activities.',
tags: ['security', 'logging'],
},
{
name: 'user-analytics',
owner: 'frontend-team',
type: 'website',
description:
'Analytics dashboard for user behavior and engagement metrics.',
tags: ['analytics', 'user', 'metrics'],
},
{
name: 'data-cleaner',
owner: 'data-team',
type: 'service',
description: 'Service for cleaning and standardizing data.',
tags: ['data', 'cleaning'],
},
{
name: 'infrastructure-deployer',
owner: 'devops-team',
type: 'other',
description: 'Tools for deploying and managing infrastructure resources.',
tags: ['infrastructure', 'deployment', 'devops'],
},
{
name: 'notification-queue',
owner: 'communication-team',
type: 'service',
description: 'Queue system for managing notification delivery.',
tags: ['notifications', 'queue'],
},
{
name: 'analytics-exporter',
owner: 'analytics-team',
type: 'service',
description: 'Service for exporting analytics data in various formats.',
tags: ['analytics', 'export'],
},
{
name: 'file-validator',
owner: 'storage-team',
type: 'service',
description: 'Service for validating file integrity and format.',
tags: ['files', 'validation'],
},
{
name: 'search-optimizer',
owner: 'search-team',
type: 'service',
description: 'Service for optimizing search performance and relevance.',
tags: ['search', 'optimization'],
},
{
name: 'mobile-analytics',
owner: 'mobile-team',
type: 'service',
description: 'Analytics service specifically for mobile applications.',
tags: ['mobile', 'analytics'],
},
{
name: 'system-logger',
owner: 'devops-team',
type: 'service',
description: 'Service for logging system events and activities.',
tags: ['logging', 'system'],
},
{
name: 'media-validator',
owner: 'media-team',
type: 'service',
description: 'Service for validating media files and formats.',
tags: ['media', 'validation'],
},
{
name: 'user-audit',
owner: 'security-team',
type: 'service',
description: 'Service for auditing user activities and access.',
tags: ['audit', 'user', 'security'],
},
{
name: 'data-normalizer',
owner: 'data-team',
type: 'service',
description: 'Service for normalizing data formats and structures.',
tags: ['data', 'normalization'],
},
{
name: 'admin-authorization',
owner: 'platform-team',
type: 'service',
description: 'Authorization service for administrative functions.',
tags: ['admin', 'authorization', 'security'],
},
{
name: 'test-reporting',
owner: 'qa-team',
type: 'other',
description: 'Tools for generating and managing test reports.',
tags: ['testing', 'reporting', 'qa'],
},
{
name: 'event-aggregator',
owner: 'platform-team',
type: 'service',
description: 'Service for aggregating and processing events.',
tags: ['events', 'aggregation'],
},
{
name: 'payment-reconciler',
owner: 'finance-team',
type: 'service',
description: 'Service for reconciling payment transactions.',
tags: ['payments', 'reconciliation', 'finance'],
},
{
name: 'document-validator',
owner: 'frontend-team',
type: 'service',
description: 'Service for validating document formats and content.',
tags: ['documents', 'validation'],
},
{
name: 'service-monitor',
owner: 'devops-team',
type: 'service',
description: 'Service for monitoring service health and performance.',
tags: ['monitoring', 'services', 'health'],
},
{
name: 'security-validator',
owner: 'security-team',
type: 'service',
description: 'Service for validating security configurations and policies.',
tags: ['security', 'validation'],
},
];
@@ -45,11 +45,8 @@ export const Heading = forwardRef<HTMLHeadingElement, HeadingProps>(
return (
<Component
ref={ref}
className={clsx(
'canon-Heading',
responsiveVariant && `canon-Heading--variant-${responsiveVariant}`,
className,
)}
className={clsx('canon-Heading', className)}
data-variant={responsiveVariant}
{...restProps}
>
{children}
@@ -22,32 +22,32 @@
margin: 0;
}
.canon-Heading--variant-display {
.canon-Heading[data-variant='display'] {
font-size: var(--canon-font-size-10);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title1 {
.canon-Heading[data-variant='title1'] {
font-size: var(--canon-font-size-9);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title2 {
.canon-Heading[data-variant='title2'] {
font-size: var(--canon-font-size-8);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title3 {
.canon-Heading[data-variant='title3'] {
font-size: var(--canon-font-size-7);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title4 {
.canon-Heading[data-variant='title4'] {
font-size: var(--canon-font-size-title4);
font-weight: var(--canon-font-weight-bold);
}
.canon-Heading--variant-title5 {
.canon-Heading[data-variant='title5'] {
font-size: var(--canon-font-size-5);
font-weight: var(--canon-font-weight-bold);
}
@@ -39,16 +39,13 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
return (
<button
ref={ref}
className={clsx(
'canon-IconButton',
`canon-IconButton--size-${responsiveSize}`,
`canon-IconButton--variant-${responsiveVariant}`,
className,
)}
className={clsx('canon-IconButton', className)}
data-size={responsiveSize}
data-variant={responsiveVariant}
style={style}
{...rest}
>
<Icon name={icon} className="canon-IconButton--icon" />
<Icon name={icon} className="canon-IconButtonIcon" />
</button>
);
},
@@ -32,7 +32,7 @@
}
}
.canon-IconButton--variant-primary {
.canon-IconButton[data-variant='primary'] {
background-color: var(--canon-bg-solid);
color: var(--canon-fg-solid);
transition: background-color 150ms ease, box-shadow 150ms ease;
@@ -56,7 +56,7 @@
}
}
.canon-IconButton--variant-secondary {
.canon-IconButton[data-variant='secondary'] {
background-color: var(--canon-bg-surface-1);
box-shadow: inset 0 0 0 1px var(--canon-border);
color: var(--canon-fg-primary);
@@ -82,24 +82,24 @@
}
}
.canon-IconButton--size-medium {
.canon-IconButton[data-size='medium'] {
font-size: var(--canon-font-size-4);
height: 40px;
width: 40px;
}
.canon-IconButton--size-small {
.canon-IconButton[data-size='small'] {
font-size: var(--canon-font-size-3);
height: 32px;
width: 32px;
}
.canon-IconButton--size-small .canon-IconButton--icon {
.canon-IconButton[data-size='small'] .canon-IconButtonIcon {
width: 1rem;
height: 1rem;
}
.canon-IconButton--size-medium .canon-IconButton--icon {
.canon-IconButton[data-size='medium'] .canon-IconButtonIcon {
width: 1.5rem;
height: 1.5rem;
}
+3 -8
View File
@@ -38,14 +38,9 @@ export const Link = forwardRef<HTMLElement, LinkProps>((props, ref) => {
const { renderElement } = useRender({
render,
props: {
className: clsx(
'canon-Link',
responsiveVariant && `canon-Link--variant-${responsiveVariant}`,
responsiveWeight && `canon-Link--weight-${responsiveWeight}`,
className,
),
responsiveVariant,
responsiveWeight,
className: clsx('canon-Link', className),
['data-variant']: responsiveVariant,
['data-weight']: responsiveWeight,
...restProps,
},
refs: [ref, internalRef],
@@ -36,30 +36,30 @@
}
}
.canon-Link--variant-body {
.canon-Link[data-variant='body'] {
font-size: var(--canon-font-size-3);
line-height: 140%;
}
.canon-Link--variant-subtitle {
.canon-Link[data-variant='subtitle'] {
font-size: var(--canon-font-size-4);
line-height: 140%;
}
.canon-Link--variant-caption {
.canon-Link[data-variant='caption'] {
font-size: var(--canon-font-size-2);
line-height: 140%;
}
.canon-Link--variant-label {
.canon-Link[data-variant='label'] {
font-size: var(--canon-font-size-1);
line-height: 140%;
}
.canon-Link--weight-regular {
.canon-Link[data-weight='regular'] {
font-weight: var(--canon-font-weight-regular);
}
.canon-Link--weight-bold {
.canon-Link[data-weight='bold'] {
font-weight: var(--canon-font-weight-bold);
}
@@ -21,14 +21,14 @@
width: 100%;
}
.canon-Select--label {
.canon-SelectLabel {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-primary);
margin-bottom: var(--canon-space-1_5);
}
.canon-Select--description {
.canon-SelectDescription {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-secondary);
@@ -36,7 +36,7 @@
padding-top: var(--canon-space-1_5);
}
.canon-Select--error {
.canon-SelectError {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-danger);
@@ -44,7 +44,7 @@
padding-top: var(--canon-space-1_5);
}
.canon-Select--trigger {
.canon-SelectTrigger {
box-sizing: border-box;
border-radius: var(--canon-radius-3);
border: 1px solid var(--canon-border);
@@ -63,41 +63,41 @@
gap: var(--canon-space-2);
}
.canon-Select--trigger::placeholder {
.canon-SelectTrigger::placeholder {
color: var(--canon-fg-secondary);
}
.canon-Select--trigger:hover {
.canon-SelectTrigger:hover {
border-color: var(--canon-border-hover);
}
.canon-Select--trigger:focus-visible {
.canon-SelectTrigger:focus-visible {
border-color: var(--canon-border-pressed);
outline: 0;
}
.canon-Select--trigger[data-invalid] {
.canon-SelectTrigger[data-invalid] {
border-color: var(--canon-fg-danger);
}
.canon-Select--trigger[data-invalid]:hover {
.canon-SelectTrigger[data-invalid]:hover {
border-width: 2px;
}
.canon-Select--trigger[data-invalid]:focus-visible {
.canon-SelectTrigger[data-invalid]:focus-visible {
border-width: 2px;
}
.canon-Select--trigger[data-disabled] {
.canon-SelectTrigger[data-disabled] {
cursor: not-allowed;
border-color: var(--canon-border-disabled);
color: var(--canon-fg-disabled);
}
.canon-Select--trigger-size-small {
.canon-SelectTrigger[data-size='small'] {
height: 2rem;
}
.canon-Select--trigger-size-medium {
.canon-SelectTrigger[data-size='medium'] {
height: 3rem;
}
@@ -106,11 +106,11 @@
transition: transform 0.2s ease;
}
.canon-Select--trigger[data-popup-open] .canon-SelectIcon {
.canon-SelectTrigger[data-popup-open] .canon-SelectIcon {
transform: rotate(180deg);
}
.canon-Select--popup {
.canon-SelectPopup {
box-sizing: border-box;
max-height: var(--available-height);
overflow-y: auto;
@@ -125,13 +125,13 @@
transition: transform 150ms, opacity 150ms;
}
.canon-Select--popup[data-starting-style],
.canon-Select--popup[data-ending-style] {
.canon-SelectPopup[data-starting-style],
.canon-SelectPopup[data-ending-style] {
opacity: 0;
transform: scale(0.9);
}
.canon-Select--item {
.canon-SelectItem {
position: relative;
width: var(--anchor-width);
display: grid;
@@ -149,13 +149,13 @@
outline: none;
}
.canon-Select--item[data-highlighted] {
.canon-SelectItem[data-highlighted] {
z-index: 0;
position: relative;
color: var(--canon-fg-primary);
}
.canon-Select--item[data-highlighted]::before {
.canon-SelectItem[data-highlighted]::before {
content: '';
z-index: -1;
position: absolute;
@@ -165,37 +165,37 @@
background-color: var(--canon-bg-tint-hover);
}
.canon-Select--item[data-disabled] {
.canon-SelectItem[data-disabled] {
cursor: not-allowed;
color: var(--canon-fg-disabled);
}
.canon-Select--item-indicator {
.canon-SelectItemIndicator {
grid-area: icon;
display: flex;
align-items: center;
justify-content: center;
}
.canon-Select--item-text {
.canon-SelectItemText {
flex: 1;
grid-area: text;
}
.canon-Select--required {
.canon-SelectRequired {
color: var(--canon-fg-secondary);
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
margin-left: var(--canon-space-1);
}
.canon-Select--icon {
.canon-SelectIcon {
display: flex;
align-items: center;
justify-content: center;
}
.canon-Select--value {
.canon-SelectValue {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
+12 -14
View File
@@ -48,10 +48,10 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>((props, ref) => {
return (
<div className={clsx('canon-Select', className)} style={style} ref={ref}>
{label && (
<label className="canon-Select--label" htmlFor={selectId}>
<label className="canon-SelectLabel" htmlFor={selectId}>
{label}
{required && (
<span aria-hidden="true" className="canon-Select--required">
<span aria-hidden="true" className="canon-SelectRequired">
(Required)
</span>
)}
@@ -60,35 +60,33 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>((props, ref) => {
<SelectPrimitive.Root {...rest}>
<SelectPrimitive.Trigger
id={selectId}
className={clsx('canon-Select--trigger', {
'canon-Select--trigger-size-small': responsiveSize === 'small',
'canon-Select--trigger-size-medium': responsiveSize === 'medium',
})}
className="canon-SelectTrigger"
data-size={responsiveSize}
data-invalid={error}
>
<SelectPrimitive.Value
className="canon-Select--value"
className="canon-SelectValue"
placeholder={placeholder}
/>
<SelectPrimitive.Icon className="canon-Select--icon">
<SelectPrimitive.Icon className="canon-SelectIcon">
<Icon name="chevron-down" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
<SelectPrimitive.Portal>
<SelectPrimitive.Backdrop />
<SelectPrimitive.Positioner>
<SelectPrimitive.Popup className="canon-Select--popup">
<SelectPrimitive.Popup className="canon-SelectPopup">
{options?.map(option => (
<SelectPrimitive.Item
key={option.value}
value={option.value}
disabled={option.disabled}
className="canon-Select--item"
className="canon-SelectItem"
>
<SelectPrimitive.ItemIndicator className="canon-Select--item-indicator">
<SelectPrimitive.ItemIndicator className="canon-SelectItemIndicator">
<Icon name="check" />
</SelectPrimitive.ItemIndicator>
<SelectPrimitive.ItemText className="canon-Select--item-text">
<SelectPrimitive.ItemText className="canon-SelectItemText">
{option.label}
</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
@@ -98,12 +96,12 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>((props, ref) => {
</SelectPrimitive.Portal>
</SelectPrimitive.Root>
{description && (
<p className="canon-Select--description" id={descriptionId}>
<p className="canon-SelectDescription" id={descriptionId}>
{description}
</p>
)}
{error && (
<p className="canon-Select--error" id={errorId} role="alert">
<p className="canon-SelectError" id={errorId} role="alert">
{error}
</p>
)}
@@ -14,6 +14,10 @@
* limitations under the License.
*/
import { ComponentType } from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { Table } from '../Table';
const invoices = [
{
invoice: 'INV001',
@@ -59,28 +63,15 @@ const invoices = [
},
];
import { ComponentType } from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import {
Table,
TableBody,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from '../Table';
const meta = {
title: 'Components/Table',
component: Table,
component: Table.Root,
subcomponents: {
TableBody: TableBody as ComponentType<unknown>,
TableCell: TableCell as ComponentType<unknown>,
TableFooter: TableFooter as ComponentType<unknown>,
TableHead: TableHead as ComponentType<unknown>,
TableHeader: TableHeader as ComponentType<unknown>,
TableRow: TableRow as ComponentType<unknown>,
Body: Table.Body as ComponentType<unknown>,
Cell: Table.Cell as ComponentType<unknown>,
Head: Table.Head as ComponentType<unknown>,
Header: Table.Header as ComponentType<unknown>,
Row: Table.Row as ComponentType<unknown>,
},
} satisfies Meta<typeof Table>;
@@ -89,31 +80,27 @@ type Story = StoryObj<typeof meta>;
export const Default: Story = {
render: () => (
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Invoice</TableHead>
<TableHead>Status</TableHead>
<TableHead>Method</TableHead>
<TableHead className="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<Table.Root>
<Table.Header>
<Table.Row>
<Table.Head className="w-[100px]">Invoice</Table.Head>
<Table.Head>Status</Table.Head>
<Table.Head>Method</Table.Head>
<Table.Head className="text-right">Amount</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{invoices.map(invoice => (
<TableRow key={invoice.invoice}>
<TableCell className="font-medium">{invoice.invoice}</TableCell>
<TableCell>{invoice.paymentStatus}</TableCell>
<TableCell>{invoice.paymentMethod}</TableCell>
<TableCell className="text-right">{invoice.totalAmount}</TableCell>
</TableRow>
<Table.Row key={invoice.invoice}>
<Table.Cell className="font-medium">{invoice.invoice}</Table.Cell>
<Table.Cell>{invoice.paymentStatus}</Table.Cell>
<Table.Cell>{invoice.paymentMethod}</Table.Cell>
<Table.Cell className="text-right">
{invoice.totalAmount}
</Table.Cell>
</Table.Row>
))}
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>Total</TableCell>
<TableCell className="text-right">$2,500.00</TableCell>
</TableRow>
</TableFooter>
</Table>
</Table.Body>
</Table.Root>
),
};
+28 -48
View File
@@ -13,103 +13,83 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { forwardRef } from 'react';
/** @public */
const Table = forwardRef<
import { forwardRef } from 'react';
import clsx from 'clsx';
import { TableCell } from './TableCell/TableCell';
import { TableCellText } from './TableCellText/TableCellText';
import { TableCellLink } from './TableCellLink/TableCellLink';
const TableRoot = forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="table">
<table ref={ref} className={className} {...props} />
</div>
<table ref={ref} className={clsx('canon-TableRoot', className)} {...props} />
));
Table.displayName = 'Table';
TableRoot.displayName = 'TableRoot';
/** @public */
const TableHeader = forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead
ref={ref}
className={['table-header', className].join(' ')}
className={clsx('canon-TableHeader', className)}
{...props}
/>
));
TableHeader.displayName = 'TableHeader';
/** @public */
const TableBody = forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody ref={ref} className={['table-body', className].join(' ')} {...props} />
<tbody ref={ref} className={clsx('canon-TableBody', className)} {...props} />
));
TableBody.displayName = 'TableBody';
/** @public */
const TableFooter = forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={['table-footer', className].join(' ')}
{...props}
/>
));
TableFooter.displayName = 'TableFooter';
/** @public */
const TableRow = forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr ref={ref} className={['table-row', className].join(' ')} {...props}>
<tr ref={ref} className={clsx('canon-TableRow', className)} {...props}>
{props.children}
</tr>
));
TableRow.displayName = 'TableRow';
/** @public */
const TableHead = forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th ref={ref} className={['table-head', className].join(' ')} {...props} />
<th ref={ref} className={clsx('canon-TableHead', className)} {...props} />
));
TableHead.displayName = 'TableHead';
/** @public */
const TableCell = forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td ref={ref} className={['table-cell', className].join(' ')} {...props} />
));
TableCell.displayName = 'TableCell';
/** @public */
const TableCaption = forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={['table-caption', className].join(' ')}
className={clsx('canon-TableCaption', className)}
{...props}
/>
));
TableCaption.displayName = 'TableCaption';
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
/**
* Table component for displaying tabular data
* @public
*/
export const Table = {
Root: TableRoot,
Header: TableHeader,
Body: TableBody,
Head: TableHead,
Row: TableRow,
Cell: TableCell,
CellText: TableCellText,
CellLink: TableCellLink,
Caption: TableCaption,
};
@@ -0,0 +1,32 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react';
import { TableCell } from './TableCell';
const meta = {
title: 'Components/Table/TableCell',
component: TableCell,
} satisfies Meta<typeof TableCell>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
children: 'Hello world',
},
};
@@ -0,0 +1,20 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.canon-TableCell {
padding: var(--canon-space-3);
font-size: var(--canon-font-size-3);
}
@@ -0,0 +1,29 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { forwardRef } from 'react';
import clsx from 'clsx';
/** @public */
const TableCell = forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td ref={ref} className={clsx('canon-TableCell', className)} {...props} />
));
TableCell.displayName = 'TableCell';
export { TableCell };
@@ -0,0 +1,40 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react';
import { TableCellLink } from './TableCellLink';
const meta = {
title: 'Components/Table/TableCellLink',
component: TableCellLink,
} satisfies Meta<typeof TableCellLink>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
title: 'I am a link',
href: 'https://canon.backstage.io',
},
};
export const WithDescription: Story = {
args: {
...Default.args,
description: 'This is a description',
},
};
@@ -0,0 +1,21 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.canon-TableCellLink {
display: flex;
flex-direction: column;
gap: var(--canon-space-0_5);
}
@@ -0,0 +1,46 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { forwardRef } from 'react';
import clsx from 'clsx';
import { TableCellLinkProps } from './types';
import { Text } from '../../Text/Text';
import { Link } from '../../Link/Link';
/** @public */
const TableCellLink = forwardRef<HTMLDivElement, TableCellLinkProps>(
({ className, title, description, href, render, ...props }, ref) => (
<div
ref={ref}
className={clsx('canon-TableCellLink', className)}
{...props}
>
{title && (
<Link href={href} render={render}>
{title}
</Link>
)}
{description && (
<Text variant="body" color="secondary">
{description}
</Text>
)}
</div>
),
);
TableCellLink.displayName = 'TableCellLink';
export { TableCellLink };
@@ -0,0 +1,26 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { useRender } from '@base-ui-components/react/use-render';
/** @public */
export interface TableCellLinkProps
extends React.TdHTMLAttributes<HTMLTableCellElement> {
title: string;
description?: string;
href: string;
render?: useRender.ComponentProps<'a'>['render'];
}
@@ -0,0 +1,39 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react';
import { TableCellText } from './TableCellText';
const meta = {
title: 'Components/Table/TableCellText',
component: TableCellText,
} satisfies Meta<typeof TableCellText>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
title: 'Hello world',
},
};
export const WithDescription: Story = {
args: {
...Default.args,
description: 'This is a description',
},
};
@@ -0,0 +1,21 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.canon-TableCellText {
display: flex;
flex-direction: column;
gap: var(--canon-space-0_5);
}
@@ -0,0 +1,41 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { forwardRef } from 'react';
import clsx from 'clsx';
import { TableCellTextProps } from './types';
import { Text } from '../../Text/Text';
/** @public */
const TableCellText = forwardRef<HTMLDivElement, TableCellTextProps>(
({ className, title, description, ...props }, ref) => (
<div
ref={ref}
className={clsx('canon-TableCellText', className)}
{...props}
>
{title && <Text variant="body">{title}</Text>}
{description && (
<Text variant="body" color="secondary">
{description}
</Text>
)}
</div>
),
);
TableCellText.displayName = 'TableCellText';
export { TableCellText };
@@ -0,0 +1,22 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/** @public */
export interface TableCellTextProps
extends React.TdHTMLAttributes<HTMLTableCellElement> {
title: string;
description?: string;
}
+4 -9
View File
@@ -13,12 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export {
Table,
TableHead,
TableRow,
TableHeader,
TableCell,
TableBody,
TableFooter,
} from './Table';
export * from './Table';
export * from './TableCellText/types';
export * from './TableCellLink/types';
+12 -48
View File
@@ -1,61 +1,25 @@
.table {
position: relative;
overflow: auto;
.canon-TableRoot {
width: 100%;
background-color: var(--canon-bg-surface-1);
border-radius: var(--canon-radius-2);
padding-bottom: var(--canon-space-0_5);
padding-top: var(--canon-space-0_5);
font-size: var(--canon-font-size-3);
font-family: var(--canon-font-regular);
font-weight: var(--canon-font-weight-regular);
table {
width: 100%;
caption-side: bottom;
font-size: var(--canon-font-size-sm);
border-collapse: collapse;
}
caption-side: bottom;
border-collapse: collapse;
}
.table-head {
.canon-TableHead {
text-align: left;
padding: var(--canon-space-3);
font-size: var(--canon-font-size-3);
color: var(--canon-fg-primary);
}
.table-body {
tr:last-child {
border-bottom: none;
}
.canon-TableBody {
color: var(--canon-fg-primary);
}
.table-row {
.canon-TableRow {
border-bottom: 1px solid var(--canon-border);
transition: color 0.2s ease-in-out;
&:hover td {
background-color: var(--canon-bg-tint);
}
& .table-cell:first-child {
border-top-left-radius: var(--canon-radius-2);
border-bottom-left-radius: var(--canon-radius-2);
box-shadow: inset 4px 2px 0 0 var(--canon-bg-surface-1),
inset 4px -2px 0 0 var(--canon-bg-surface-1);
padding-left: var(--canon-space-3);
}
& .table-cell:last-child {
border-top-right-radius: var(--canon-radius-2);
border-bottom-right-radius: var(--canon-radius-2);
box-shadow: inset -4px 2px 0 0 var(--canon-bg-surface-1),
inset -4px -2px 0 0 var(--canon-bg-surface-1);
padding-right: var(--canon-space-3);
}
}
.table-cell {
padding: var(--canon-space-3);
background-color: var(--canon-bg);
box-shadow: inset 0px 2px 0 0 var(--canon-bg-surface-1),
inset 0px -2px 0 0 var(--canon-bg-surface-1);
.canon-TableBody .canon-TableRow:hover {
background-color: var(--canon-gray-2);
}
+4 -7
View File
@@ -41,13 +41,10 @@ export const Text = forwardRef<HTMLParagraphElement, TextProps>(
return (
<p
ref={ref}
className={clsx(
'canon-Text',
responsiveVariant && `canon-Text--variant-${responsiveVariant}`,
responsiveWeight && `canon-Text--weight-${responsiveWeight}`,
responsiveColor && `canon-Text--color-${responsiveColor}`,
className,
)}
className={clsx('canon-Text', className)}
data-variant={responsiveVariant}
data-weight={responsiveWeight}
data-color={responsiveColor}
style={style}
{...restProps}
>
+11 -12
View File
@@ -16,55 +16,54 @@
.canon-Text {
font-family: var(--canon-font-regular);
color: var(--canon-fg-primary);
padding: 0;
margin: 0;
}
.canon-Text--variant-body {
.canon-Text[data-variant='body'] {
font-size: var(--canon-font-size-3);
line-height: 140%;
}
.canon-Text--variant-subtitle {
.canon-Text[data-variant='subtitle'] {
font-size: var(--canon-font-size-4);
line-height: 140%;
}
.canon-Text--variant-caption {
.canon-Text[data-variant='caption'] {
font-size: var(--canon-font-size-2);
line-height: 140%;
}
.canon-Text--variant-label {
.canon-Text[data-variant='label'] {
font-size: var(--canon-font-size-1);
line-height: 140%;
}
.canon-Text--weight-regular {
.canon-Text[data-weight='regular'] {
font-weight: var(--canon-font-weight-regular);
}
.canon-Text--weight-bold {
.canon-Text[data-weight='bold'] {
font-weight: var(--canon-font-weight-bold);
}
.canon-Text--color-primary {
.canon-Text[data-color='primary'] {
color: var(--canon-fg-primary);
}
.canon-Text--color-secondary {
.canon-Text[data-color='secondary'] {
color: var(--canon-fg-secondary);
}
.canon-Text--color-danger {
.canon-Text[data-color='danger'] {
color: var(--canon-fg-danger);
}
.canon-Text--color-warning {
.canon-Text[data-color='warning'] {
color: var(--canon-fg-warning);
}
.canon-Text--color-success {
.canon-Text[data-color='success'] {
color: var(--canon-fg-success);
}
@@ -21,14 +21,14 @@
width: 100%;
}
.canon-TextField--label {
.canon-TextFieldLabel {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-primary);
margin-bottom: var(--canon-space-1_5);
}
.canon-TextField--description {
.canon-TextFieldDescription {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-secondary);
@@ -36,7 +36,7 @@
padding-top: var(--canon-space-1_5);
}
.canon-TextField--error {
.canon-TextFieldError {
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
color: var(--canon-fg-danger);
@@ -44,7 +44,7 @@
padding-top: var(--canon-space-1_5);
}
.canon-TextField--input {
.canon-TextFieldInput {
border-radius: var(--canon-radius-3);
border: 1px solid var(--canon-border);
padding: 0 var(--canon-space-4);
@@ -57,39 +57,39 @@
width: 100%;
}
.canon-TextField--input::placeholder {
.canon-TextFieldInput::placeholder {
color: var(--canon-fg-secondary);
}
.canon-TextField--input:hover {
.canon-TextFieldInput:hover {
border-color: var(--canon-border-hover);
}
.canon-TextField--input:focus-visible {
.canon-TextFieldInput:focus-visible {
outline-color: var(--canon-border-pressed);
outline-width: 0px;
border-color: var(--canon-border-pressed);
}
.canon-TextField--input[data-invalid] {
.canon-TextFieldInput[data-invalid] {
border-color: var(--canon-fg-danger);
}
.canon-TextField--input[data-disabled] {
.canon-TextFieldInput[data-disabled] {
opacity: 0.5;
cursor: not-allowed;
border: 1px solid var(--canon-border-disabled);
}
.canon-TextField--input-size-small {
.canon-TextFieldInput[data-size='small'] {
height: 2rem;
}
.canon-TextField--input-size-medium {
.canon-TextFieldInput[data-size='medium'] {
height: 2.5rem;
}
.canon-TextField--required {
.canon-TextFieldRequired {
color: var(--canon-fg-secondary);
font-size: var(--canon-font-size-2);
font-weight: var(--canon-font-weight-regular);
@@ -50,10 +50,10 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps>(
ref={ref}
>
{label && (
<label className="canon-TextField--label" htmlFor={inputId}>
<label className="canon-TextFieldLabel" htmlFor={inputId}>
{label}
{required && (
<span aria-hidden="true" className="canon-TextField--required">
<span aria-hidden="true" className="canon-TextFieldRequired">
(Required)
</span>
)}
@@ -61,10 +61,8 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps>(
)}
<Input
id={inputId}
className={clsx('canon-TextField--input', {
'canon-TextField--input-size-small': responsiveSize === 'small',
'canon-TextField--input-size-medium': responsiveSize === 'medium',
})}
className="canon-TextFieldInput"
data-size={responsiveSize}
aria-labelledby={label ? inputId : undefined}
aria-describedby={clsx({
[descriptionId]: description,
@@ -75,12 +73,12 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps>(
{...rest}
/>
{description && (
<p className="canon-TextField--description" id={descriptionId}>
<p className="canon-TextFieldDescription" id={descriptionId}>
{description}
</p>
)}
{error && (
<p className="canon-TextField--error" id={errorId} role="alert">
<p className="canon-TextFieldError" id={errorId} role="alert">
{error}
</p>
)}
+5
View File
@@ -16,12 +16,17 @@
@import '../components/Box/styles.css';
@import '../components/Button/styles.css';
@import '../components/DataTable/Root/DataTableRoot.styles.css';
@import '../components/DataTable/Pagination/DataTablePagination.styles.css';
@import '../components/Flex/styles.css';
@import '../components/Grid/styles.css';
@import '../components/Container/styles.css';
@import '../components/Icon/styles.css';
@import '../components/Checkbox/styles.css';
@import '../components/Table/styles.css';
@import '../components/Table/TableCell/TableCell.styles.css';
@import '../components/Table/TableCellText/TableCellText.styles.css';
@import '../components/Table/TableCellLink/TableCellLink.styles.css';
@import '../components/Text/styles.css';
@import '../components/Heading/styles.css';
@import '../components/IconButton/styles.css';
+1
View File
@@ -33,6 +33,7 @@ export * from './components/Heading';
// UI components
export * from './components/Button';
export * from './components/DataTable';
export * from './components/Icon';
export * from './components/IconButton';
export * from './components/Checkbox';
@@ -46,10 +46,12 @@ function verifyIndex(pkg: string, packageJson?: BackstagePackageJson) {
console.log(`Verifying ${pkg}`);
const tsPath = path.join(pkg, 'src/index.ts');
const sourceFile = project.getSourceFile(tsPath);
if (!sourceFile) {
console.log(`Could not find ${tsPath}`);
process.exit(1);
}
const symbols = sourceFile?.getExportSymbols();
const exportCount = symbols?.length || 0;
@@ -69,15 +71,34 @@ function verifyIndex(pkg: string, packageJson?: BackstagePackageJson) {
console.log(' ❌ Missing default export');
}
let createRouterDeprecated = undefined;
let routerCreateRouterDeprecated = undefined;
if (createRouterExport) {
createRouterDeprecated = createRouterExport
.getJsDocTags()
.find(tag => tag.getName() === 'deprecated');
const declarations = createRouterExport?.getDeclarations();
const firstDeclaration = declarations?.[0];
let resolvedSymbol = undefined;
if (firstDeclaration) {
// Try resolving to the definition directly
resolvedSymbol = createRouterExport.getAliasedSymbol();
if (resolvedSymbol) {
const resolvedDeclarations = resolvedSymbol.getDeclarations();
const resolvedDeclaration = resolvedDeclarations?.[0];
if (resolvedDeclaration) {
routerCreateRouterDeprecated = resolvedDeclaration
.getSymbol()
?.getJsDocTags()
.find(tag => tag.getName() === 'deprecated');
}
}
}
}
if (createRouterExport) {
console.log(' ❌ createRouter is exported');
if (!createRouterDeprecated)
if (!createRouterDeprecated && !routerCreateRouterDeprecated)
console.log(' ❌ createRouter is NOT deprecated');
}
+27 -7
View File
@@ -3789,6 +3789,7 @@ __metadata:
"@storybook/react": "npm:^8.6.8"
"@storybook/react-webpack5": "npm:^8.6.8"
"@storybook/test": "npm:^8.6.8"
"@tanstack/react-table": "npm:^8.21.2"
"@types/react": "npm:^18.0.0"
"@types/react-dom": "npm:^18.0.0"
chalk: "npm:^5.4.1"
@@ -16446,20 +16447,20 @@ __metadata:
linkType: hard
"@rspack/dev-server@npm:^1.0.9":
version: 1.1.1
resolution: "@rspack/dev-server@npm:1.1.1"
version: 1.1.0
resolution: "@rspack/dev-server@npm:1.1.0"
dependencies:
chokidar: "npm:^3.6.0"
express: "npm:^4.21.2"
http-proxy-middleware: "npm:^2.0.7"
express: "npm:^4.19.2"
http-proxy-middleware: "npm:^2.0.6"
mime-types: "npm:^2.1.35"
p-retry: "npm:^6.2.0"
webpack-dev-middleware: "npm:^7.4.2"
webpack-dev-server: "npm:5.2.0"
ws: "npm:^8.18.0"
ws: "npm:^8.16.0"
peerDependencies:
"@rspack/core": "*"
checksum: 10/a712c1ed6819da26984d106e7fdede3ed720ef177bf8fa976e91eb95988b862b306c354829486dce8eabde420cb5a96cb25ecf397c9532f87f3b913e9a9da40c
checksum: 10/9619dc416e4bb46f13a47c62725ab538ef8c1e02240437bf91394b6dbe3f0f915ad05c5bdda72501b813cb9ff86989bf9b295efee8e39323f6626573f53af459
languageName: node
linkType: hard
@@ -19237,6 +19238,25 @@ __metadata:
languageName: node
linkType: hard
"@tanstack/react-table@npm:^8.21.2":
version: 8.21.2
resolution: "@tanstack/react-table@npm:8.21.2"
dependencies:
"@tanstack/table-core": "npm:8.21.2"
peerDependencies:
react: ">=16.8"
react-dom: ">=16.8"
checksum: 10/62b944b79f0ac06b74921c1da4821a4f122f7b30e2bd3391b5eac2d15a8050f3ac7752282354520ec42a3fa382580ac88e28731121ad445bb65218614ab21fd1
languageName: node
linkType: hard
"@tanstack/table-core@npm:8.21.2":
version: 8.21.2
resolution: "@tanstack/table-core@npm:8.21.2"
checksum: 10/6a435905fcfa2ac0d06963f14125f313a08086cb0c71949d60fbd04c3a5e229779f94ff6783e35cf09f77fddfbe987cfdd74b18577ac7476b5a43729f3e4b9d5
languageName: node
linkType: hard
"@techdocs/cli@workspace:*, @techdocs/cli@workspace:packages/techdocs-cli":
version: 0.0.0-use.local
resolution: "@techdocs/cli@workspace:packages/techdocs-cli"
@@ -48115,7 +48135,7 @@ __metadata:
languageName: node
linkType: hard
"ws@npm:*, ws@npm:^8.11.0, ws@npm:^8.12.0, ws@npm:^8.13.0, ws@npm:^8.17.1, ws@npm:^8.18.0, ws@npm:^8.2.3, ws@npm:^8.8.0":
"ws@npm:*, ws@npm:^8.11.0, ws@npm:^8.12.0, ws@npm:^8.13.0, ws@npm:^8.16.0, ws@npm:^8.17.1, ws@npm:^8.18.0, ws@npm:^8.2.3, ws@npm:^8.8.0":
version: 8.18.1
resolution: "ws@npm:8.18.1"
peerDependencies: