agroptima-design-system 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/design-system-deploy-production.yaml +7 -7
- package/.storybook/main.ts +24 -24
- package/.storybook/preview.ts +11 -0
- package/package.json +2 -2
- package/src/atoms/CardsTableList.scss +99 -0
- package/src/atoms/CardsTableList.tsx +69 -0
- package/src/atoms/Multiselect.scss +148 -0
- package/src/atoms/Multiselect.tsx +124 -0
- package/src/icons/checkbox-active.svg +1 -0
- package/src/icons/checkbox-inactive.svg +1 -0
- package/src/icons/index.tsx +6 -0
- package/src/icons/sorter.svg +1 -0
- package/src/settings/_typography.scss +39 -0
- package/src/stories/CardsTableList.stories.ts +83 -0
- package/src/stories/Changelog.stories.mdx +34 -1
- package/src/stories/Multiselect.stories.ts +69 -0
- package/.github/workflows/design-system-deploy-staging.yaml +0 -24
|
@@ -10,7 +10,7 @@ permissions:
|
|
|
10
10
|
contents: write
|
|
11
11
|
|
|
12
12
|
jobs:
|
|
13
|
-
|
|
13
|
+
github-pages-deployment:
|
|
14
14
|
runs-on: ubuntu-latest
|
|
15
15
|
|
|
16
16
|
steps:
|
|
@@ -19,15 +19,15 @@ jobs:
|
|
|
19
19
|
|
|
20
20
|
- name: Install dependencies and build
|
|
21
21
|
run: |
|
|
22
|
-
npm install
|
|
23
|
-
npm run
|
|
22
|
+
npm install
|
|
23
|
+
npm run build
|
|
24
24
|
- name: Add robots.txt
|
|
25
25
|
run: |
|
|
26
|
-
touch
|
|
27
|
-
echo "User-agent: *" >>
|
|
28
|
-
echo "Disallow: /" >>
|
|
26
|
+
touch storybook-static/robots.txt
|
|
27
|
+
echo "User-agent: *" >> storybook-static/robots.txt
|
|
28
|
+
echo "Disallow: /" >> storybook-static/robots.txt
|
|
29
29
|
|
|
30
30
|
- name: Deploy 🚀
|
|
31
31
|
uses: JamesIves/github-pages-deploy-action@v4
|
|
32
32
|
with:
|
|
33
|
-
folder:
|
|
33
|
+
folder: storybook-static
|
package/.storybook/main.ts
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
import type { StorybookConfig } from
|
|
2
|
-
import { join, dirname, resolve } from
|
|
1
|
+
import type { StorybookConfig } from '@storybook/nextjs'
|
|
2
|
+
import { join, dirname, resolve } from 'path'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* This function is used to resolve the absolute path of a package.
|
|
6
6
|
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
|
|
7
7
|
*/
|
|
8
8
|
function getAbsolutePath(value: string): any {
|
|
9
|
-
return dirname(require.resolve(join(value,
|
|
9
|
+
return dirname(require.resolve(join(value, 'package.json')))
|
|
10
10
|
}
|
|
11
11
|
const config: StorybookConfig = {
|
|
12
|
-
stories: [
|
|
12
|
+
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
|
|
13
13
|
addons: [
|
|
14
|
-
getAbsolutePath(
|
|
15
|
-
getAbsolutePath(
|
|
16
|
-
getAbsolutePath(
|
|
17
|
-
getAbsolutePath(
|
|
14
|
+
getAbsolutePath('@storybook/addon-links'),
|
|
15
|
+
getAbsolutePath('@storybook/addon-essentials'),
|
|
16
|
+
getAbsolutePath('@storybook/addon-onboarding'),
|
|
17
|
+
getAbsolutePath('@storybook/addon-interactions'),
|
|
18
18
|
'@storybook/addon-a11y',
|
|
19
19
|
'@storybook/addon-designs',
|
|
20
20
|
],
|
|
21
21
|
framework: {
|
|
22
|
-
name: getAbsolutePath(
|
|
22
|
+
name: getAbsolutePath('@storybook/nextjs'),
|
|
23
23
|
options: {},
|
|
24
24
|
},
|
|
25
25
|
docs: {
|
|
26
|
-
autodocs:
|
|
26
|
+
autodocs: 'tag',
|
|
27
27
|
},
|
|
28
28
|
core: {
|
|
29
|
-
builder: '@storybook/builder-webpack5'
|
|
29
|
+
builder: '@storybook/builder-webpack5',
|
|
30
30
|
},
|
|
31
31
|
webpackFinal: async (config) => {
|
|
32
|
-
const pathToInlineSvg = resolve(__dirname,
|
|
33
|
-
|
|
34
|
-
config.module = config.module || {}
|
|
35
|
-
config.module.rules = config.module.rules || []
|
|
32
|
+
const pathToInlineSvg = resolve(__dirname, '../src/icons')
|
|
33
|
+
|
|
34
|
+
config.module = config.module || {}
|
|
35
|
+
config.module.rules = config.module.rules || []
|
|
36
36
|
|
|
37
37
|
// This modifies the existing image rule to exclude .svg files
|
|
38
38
|
// since you want to handle those files with @svgr/webpack
|
|
39
|
-
const imageRule = config.module.rules.find(
|
|
39
|
+
const imageRule = config.module.rules.find(
|
|
40
|
+
(rule) => rule?.['test']?.test('.svg'),
|
|
41
|
+
)
|
|
40
42
|
if (!imageRule || typeof imageRule !== 'object') return
|
|
41
43
|
|
|
42
44
|
// Configure .svg files to be loaded with @svgr/webpack
|
|
43
|
-
config.module.rules.push(
|
|
44
|
-
{
|
|
45
|
+
config.module.rules.push({
|
|
45
46
|
...imageRule,
|
|
46
47
|
test: /\.svg$/i,
|
|
47
48
|
include: pathToInlineSvg,
|
|
48
49
|
use: ['@svgr/webpack'],
|
|
49
|
-
}
|
|
50
|
-
);
|
|
50
|
+
})
|
|
51
51
|
|
|
52
52
|
if (imageRule) {
|
|
53
|
-
imageRule['exclude'] = /\.svg$/i
|
|
53
|
+
imageRule['exclude'] = /\.svg$/i
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
return config
|
|
56
|
+
return config
|
|
57
57
|
},
|
|
58
|
-
}
|
|
59
|
-
export default config
|
|
58
|
+
}
|
|
59
|
+
export default config
|
package/.storybook/preview.ts
CHANGED
|
@@ -11,6 +11,17 @@ const preview: Preview = {
|
|
|
11
11
|
},
|
|
12
12
|
expanded: true,
|
|
13
13
|
},
|
|
14
|
+
options: {
|
|
15
|
+
storySort: {
|
|
16
|
+
order: [
|
|
17
|
+
'Welcome',
|
|
18
|
+
'Changelog',
|
|
19
|
+
'Component creation workflow',
|
|
20
|
+
'Programmers start guide',
|
|
21
|
+
'*',
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
},
|
|
14
25
|
viewport: {
|
|
15
26
|
viewports: INITIAL_VIEWPORTS,
|
|
16
27
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agroptima-design-system",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "npm run storybook",
|
|
6
6
|
"storybook": "storybook dev -p 6006 --ci",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"build-storybook": "storybook build",
|
|
9
9
|
"lint": "eslint",
|
|
10
10
|
"lint:fix": "eslint src --fix",
|
|
11
|
-
"chromatic": "npx chromatic --exit-zero-on-changes"
|
|
11
|
+
"publish-chromatic": "npx chromatic --exit-zero-on-changes"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@storybook/addon-designs": "^7.0.5",
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
@use '../settings/color_alias';
|
|
2
|
+
@use '../settings/typography';
|
|
3
|
+
@use '../settings/config';
|
|
4
|
+
|
|
5
|
+
.cards-table-list {
|
|
6
|
+
width: 100%;
|
|
7
|
+
border-collapse: separate;
|
|
8
|
+
border-spacing: 0 config.$space-3x;
|
|
9
|
+
|
|
10
|
+
thead {
|
|
11
|
+
background: transparent;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
th,
|
|
15
|
+
td {
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
text-overflow: ellipsis;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.container {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: row;
|
|
23
|
+
justify-content: space-between;
|
|
24
|
+
align-items: center;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
th {
|
|
28
|
+
padding: config.$space-2x config.$space-3x;
|
|
29
|
+
white-space: nowrap;
|
|
30
|
+
text-align: left;
|
|
31
|
+
|
|
32
|
+
.icon {
|
|
33
|
+
width: config.$icon-size-3x;
|
|
34
|
+
height: config.$icon-size-3x;
|
|
35
|
+
margin-left: config.$space-1x;
|
|
36
|
+
> svg {
|
|
37
|
+
width: 100%;
|
|
38
|
+
height: 100%;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
td {
|
|
44
|
+
padding: config.$space-5x;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
&.primary {
|
|
48
|
+
thead > tr {
|
|
49
|
+
@include typography.cards-table-list-header;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
th {
|
|
53
|
+
background: color_alias.$primary-color-600;
|
|
54
|
+
|
|
55
|
+
.icon {
|
|
56
|
+
> svg {
|
|
57
|
+
fill: color_alias.$neutral-white;
|
|
58
|
+
path {
|
|
59
|
+
fill: color_alias.$neutral-white;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
th:first-child {
|
|
66
|
+
border-radius: config.$corner-radius-xxs 0px 0px config.$corner-radius-xxs;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
th:last-child {
|
|
70
|
+
border-radius: 0px config.$corner-radius-xxs config.$corner-radius-xxs 0px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
tr {
|
|
74
|
+
box-shadow: 0px 0px 3px 0px rgba(0, 0, 0, 0.25);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
tr {
|
|
78
|
+
td {
|
|
79
|
+
@include typography.cards-table-list-text;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
tr:not(.disabled):hover {
|
|
84
|
+
background: color_alias.$primary-color-50;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
tr.disabled {
|
|
88
|
+
background: color_alias.$neutral-color-50;
|
|
89
|
+
|
|
90
|
+
td {
|
|
91
|
+
@include typography.cards-table-list-disabled-text;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
tr > td:first-child {
|
|
96
|
+
@include typography.cards-table-list-highlight-text;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import './CardsTableList.scss'
|
|
2
|
+
import { Icon, IconType } from './Icon'
|
|
3
|
+
|
|
4
|
+
export type Variant = 'primary'
|
|
5
|
+
export type Header = { label: string; icon?: IconType }
|
|
6
|
+
export type Data = {
|
|
7
|
+
[key: string]: string
|
|
8
|
+
}
|
|
9
|
+
export type Row = { id: string; isDisabled: boolean; data: Data }
|
|
10
|
+
|
|
11
|
+
export interface CardsTableListProps
|
|
12
|
+
extends React.ComponentPropsWithoutRef<'table'> {
|
|
13
|
+
headers: Header[]
|
|
14
|
+
rows: Row[]
|
|
15
|
+
variant?: Variant
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function CardsTableList({
|
|
19
|
+
headers,
|
|
20
|
+
rows,
|
|
21
|
+
summary,
|
|
22
|
+
variant = 'primary',
|
|
23
|
+
}: CardsTableListProps): React.JSX.Element {
|
|
24
|
+
const cssClasses = ['cards-table-list', variant].join(' ')
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<table summary={summary} role="table" className={cssClasses}>
|
|
28
|
+
<thead role="rowgroup">
|
|
29
|
+
<tr role="row">
|
|
30
|
+
{headers.map((header) => {
|
|
31
|
+
const { icon } = header
|
|
32
|
+
return (
|
|
33
|
+
<th
|
|
34
|
+
scope="col"
|
|
35
|
+
role="columnheader"
|
|
36
|
+
className="header"
|
|
37
|
+
key={header.label}
|
|
38
|
+
>
|
|
39
|
+
<div className="container">
|
|
40
|
+
<div className="title-container">
|
|
41
|
+
<span>{header.label}</span>
|
|
42
|
+
{icon && <Icon name={icon} />}
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</th>
|
|
46
|
+
)
|
|
47
|
+
})}
|
|
48
|
+
</tr>
|
|
49
|
+
</thead>
|
|
50
|
+
<tbody role="rowgroup">
|
|
51
|
+
{rows.map((row) => {
|
|
52
|
+
const { data, isDisabled } = row
|
|
53
|
+
const disabledClass = isDisabled ? 'disabled' : ''
|
|
54
|
+
return (
|
|
55
|
+
<tr role="row" className={`row ${disabledClass}`} key={row.id}>
|
|
56
|
+
{Object.getOwnPropertyNames(data).map((property) => {
|
|
57
|
+
return (
|
|
58
|
+
<td role="cell" key={row.id + property} className="cell">
|
|
59
|
+
{data[property]}
|
|
60
|
+
</td>
|
|
61
|
+
)
|
|
62
|
+
})}
|
|
63
|
+
</tr>
|
|
64
|
+
)
|
|
65
|
+
})}
|
|
66
|
+
</tbody>
|
|
67
|
+
</table>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
@use '../settings/color_alias';
|
|
2
|
+
@use '../settings/typography';
|
|
3
|
+
@use '../settings/config';
|
|
4
|
+
|
|
5
|
+
.multiselect-group {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
gap: config.$space-2x;
|
|
9
|
+
|
|
10
|
+
&:has(.selected-option.invalid) {
|
|
11
|
+
& .multiselect-help-text {
|
|
12
|
+
color: color_alias.$error-color-1000;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
&.primary {
|
|
17
|
+
.multiselect-label {
|
|
18
|
+
@include typography.form-label;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.selected-option {
|
|
22
|
+
border-radius: config.$corner-radius-m;
|
|
23
|
+
border: 1px solid color_alias.$neutral-color-300;
|
|
24
|
+
background: color_alias.$neutral-white;
|
|
25
|
+
@include typography.select-text;
|
|
26
|
+
|
|
27
|
+
> .icon {
|
|
28
|
+
> svg {
|
|
29
|
+
fill: color_alias.$primary-color-1000;
|
|
30
|
+
path {
|
|
31
|
+
fill: color_alias.$primary-color-1000;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
&.filled {
|
|
37
|
+
@include typography.select-option-text;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&:focus {
|
|
41
|
+
outline: color_alias.$primary-color-1000;
|
|
42
|
+
border: 1px solid color_alias.$primary-color-1000;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
&.invalid {
|
|
46
|
+
border: 1px solid color_alias.$error-color-1000;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&.disabled {
|
|
50
|
+
border: 1px solid color_alias.$neutral-color-200;
|
|
51
|
+
background: color_alias.$neutral-color-50;
|
|
52
|
+
color: color_alias.$neutral-color-200;
|
|
53
|
+
|
|
54
|
+
> .icon {
|
|
55
|
+
> svg {
|
|
56
|
+
fill: color_alias.$neutral-color-200;
|
|
57
|
+
path {
|
|
58
|
+
fill: color_alias.$neutral-color-200;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.multiselect-options {
|
|
66
|
+
border-radius: config.$corner-radius-xxs;
|
|
67
|
+
background: color_alias.$neutral-white;
|
|
68
|
+
box-shadow:
|
|
69
|
+
0px 9px 28px 8px rgba(0, 0, 0, 0.05),
|
|
70
|
+
0px 6px 16px 0px rgba(0, 0, 0, 0.08),
|
|
71
|
+
0px 3px 6px -4px rgba(0, 0, 0, 0.12);
|
|
72
|
+
|
|
73
|
+
.option {
|
|
74
|
+
background: color_alias.$neutral-white;
|
|
75
|
+
@include typography.select-option-text;
|
|
76
|
+
|
|
77
|
+
&:hover {
|
|
78
|
+
background-color: color_alias.$primary-color-50;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
> .icon {
|
|
82
|
+
> svg {
|
|
83
|
+
border-radius: config.$corner-radius-xxs;
|
|
84
|
+
.checkbox-active_svg__border {
|
|
85
|
+
fill: color_alias.$primary-color-1000;
|
|
86
|
+
}
|
|
87
|
+
.checkbox-inactive_svg__border {
|
|
88
|
+
fill: color_alias.$neutral-color-300;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.multiselect-help-text {
|
|
96
|
+
@include typography.form-help-text;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.multiselect-container {
|
|
101
|
+
display: inline-block;
|
|
102
|
+
text-align: left;
|
|
103
|
+
position: relative;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.selected-option {
|
|
107
|
+
display: flex;
|
|
108
|
+
justify-content: space-between;
|
|
109
|
+
align-items: center;
|
|
110
|
+
padding: config.$space-2x config.$space-3x;
|
|
111
|
+
cursor: default;
|
|
112
|
+
|
|
113
|
+
> .icon {
|
|
114
|
+
width: config.$icon-size-3x;
|
|
115
|
+
height: config.$icon-size-3x;
|
|
116
|
+
> svg {
|
|
117
|
+
width: 100%;
|
|
118
|
+
height: 100%;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.multiselect-options {
|
|
124
|
+
margin: 0;
|
|
125
|
+
padding: config.$space-1x 0rem;
|
|
126
|
+
text-align: left;
|
|
127
|
+
position: absolute;
|
|
128
|
+
width: 100%;
|
|
129
|
+
|
|
130
|
+
.option {
|
|
131
|
+
display: flex;
|
|
132
|
+
align-items: center;
|
|
133
|
+
cursor: default;
|
|
134
|
+
list-style-type: none;
|
|
135
|
+
padding: config.$space-2x config.$space-3x;
|
|
136
|
+
|
|
137
|
+
> .icon {
|
|
138
|
+
width: config.$icon-size-4x;
|
|
139
|
+
height: config.$icon-size-4x;
|
|
140
|
+
margin-right: config.$space-1x;
|
|
141
|
+
> svg {
|
|
142
|
+
width: 100%;
|
|
143
|
+
height: 100%;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import './Multiselect.scss'
|
|
2
|
+
import React, { useState } from 'react'
|
|
3
|
+
import { Icon } from './Icon'
|
|
4
|
+
|
|
5
|
+
export type Variant = 'primary'
|
|
6
|
+
export type Option = { id: string; label: string }
|
|
7
|
+
|
|
8
|
+
export interface MultiselectProps
|
|
9
|
+
extends React.ComponentPropsWithoutRef<'select'> {
|
|
10
|
+
placeholder?: string
|
|
11
|
+
helpText?: string
|
|
12
|
+
variant?: Variant
|
|
13
|
+
options: Option[]
|
|
14
|
+
invalid?: boolean
|
|
15
|
+
label: string
|
|
16
|
+
selectedLabel?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function Multiselect({
|
|
20
|
+
placeholder,
|
|
21
|
+
helpText,
|
|
22
|
+
variant = 'primary',
|
|
23
|
+
disabled,
|
|
24
|
+
invalid,
|
|
25
|
+
name,
|
|
26
|
+
options,
|
|
27
|
+
label,
|
|
28
|
+
selectedLabel = 'items selected',
|
|
29
|
+
}: MultiselectProps): React.JSX.Element {
|
|
30
|
+
const [showOptionsList, setShowOptionsList] = useState(false)
|
|
31
|
+
const [selectedOptionsIds, setSelectedOptionsIds] = useState<string[]>([])
|
|
32
|
+
|
|
33
|
+
const optionsListOpenClass = showOptionsList ? 'open' : ''
|
|
34
|
+
const filledSelectClass = selectedOptionsIds.length > 0 ? 'filled' : ''
|
|
35
|
+
const disabledClass = disabled ? 'disabled' : ''
|
|
36
|
+
const invalidClass = invalid ? 'invalid' : ''
|
|
37
|
+
|
|
38
|
+
const cssClasses = [
|
|
39
|
+
'selected-option',
|
|
40
|
+
optionsListOpenClass,
|
|
41
|
+
filledSelectClass,
|
|
42
|
+
disabledClass,
|
|
43
|
+
invalidClass,
|
|
44
|
+
].join(' ')
|
|
45
|
+
|
|
46
|
+
function handleOptionsList() {
|
|
47
|
+
if (!disabled) setShowOptionsList(!showOptionsList)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function selectOption(optionId: string) {
|
|
51
|
+
const hasOptionId = isOptionSelected(optionId)
|
|
52
|
+
if (hasOptionId) {
|
|
53
|
+
setSelectedOptionsIds(
|
|
54
|
+
selectedOptionsIds.filter(
|
|
55
|
+
(selectedOptionId) => selectedOptionId !== optionId,
|
|
56
|
+
),
|
|
57
|
+
)
|
|
58
|
+
} else {
|
|
59
|
+
setSelectedOptionsIds([...selectedOptionsIds, optionId])
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function handleSelectIcon() {
|
|
64
|
+
return showOptionsList ? 'AngleUp' : 'AngleDown'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isOptionSelected(optionId: string) {
|
|
68
|
+
return selectedOptionsIds.includes(optionId)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<div className={`multiselect-group ${variant}`}>
|
|
73
|
+
<span className="multiselect-label">{label}</span>
|
|
74
|
+
<div className="multiselect-container">
|
|
75
|
+
<div
|
|
76
|
+
className={cssClasses}
|
|
77
|
+
tabIndex={0}
|
|
78
|
+
onClick={handleOptionsList}
|
|
79
|
+
aria-label={label}
|
|
80
|
+
aria-live="assertive"
|
|
81
|
+
role="alert"
|
|
82
|
+
>
|
|
83
|
+
<span>
|
|
84
|
+
{selectedOptionsIds.length > 0
|
|
85
|
+
? selectedOptionsIds.length + ' ' + selectedLabel
|
|
86
|
+
: placeholder}
|
|
87
|
+
</span>
|
|
88
|
+
<Icon name={handleSelectIcon()} />
|
|
89
|
+
</div>
|
|
90
|
+
{showOptionsList && (
|
|
91
|
+
<ul className="multiselect-options" role="listbox">
|
|
92
|
+
{options.map((option) => {
|
|
93
|
+
return (
|
|
94
|
+
<li
|
|
95
|
+
className="option"
|
|
96
|
+
role="option"
|
|
97
|
+
aria-selected={isOptionSelected(option.id)}
|
|
98
|
+
data-option={option}
|
|
99
|
+
key={option.id}
|
|
100
|
+
onClick={() => selectOption(option.id)}
|
|
101
|
+
>
|
|
102
|
+
<Icon
|
|
103
|
+
name={
|
|
104
|
+
isOptionSelected(option.id)
|
|
105
|
+
? 'CheckboxActive'
|
|
106
|
+
: 'CheckboxInactive'
|
|
107
|
+
}
|
|
108
|
+
/>
|
|
109
|
+
{option.label}
|
|
110
|
+
</li>
|
|
111
|
+
)
|
|
112
|
+
})}
|
|
113
|
+
</ul>
|
|
114
|
+
)}
|
|
115
|
+
</div>
|
|
116
|
+
{helpText && (
|
|
117
|
+
<span className={`multiselect-help-text ${invalidClass}`}>
|
|
118
|
+
{helpText}
|
|
119
|
+
</span>
|
|
120
|
+
)}
|
|
121
|
+
<input type="hidden" name={name} value={selectedOptionsIds.toString()} />
|
|
122
|
+
</div>
|
|
123
|
+
)
|
|
124
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path class="border" d="M0 2a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2Z" fill="#444"/><path class="background" fill-rule="evenodd" clip-rule="evenodd" d="M18 1H2a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1ZM2 0a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2Z" fill="#444"/><path class="check" d="m7.085 13.699-3.793-3.98L2 11.063 7.085 16.4 18 4.946 16.718 3.6 7.085 13.699Z" fill="#fff"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path class="background" d="M0 2a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2Z" fill="#fff"/><path class="border" fill-rule="evenodd" clip-rule="evenodd" d="M18 1H2a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1ZM2 0a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2Z" fill="#444"/></svg>
|
package/src/icons/index.tsx
CHANGED
|
@@ -4,6 +4,8 @@ import AngleLeft from './angle-left.svg'
|
|
|
4
4
|
import AngleRight from './angle-right.svg'
|
|
5
5
|
import AngleUp from './angle-up.svg'
|
|
6
6
|
import Check from './check.svg'
|
|
7
|
+
import CheckboxActive from './checkbox-active.svg'
|
|
8
|
+
import CheckboxInactive from './checkbox-inactive.svg'
|
|
7
9
|
import Close from './close.svg'
|
|
8
10
|
import Done from './done.svg'
|
|
9
11
|
import EditColumns from './edit-columns.svg'
|
|
@@ -14,6 +16,7 @@ import Loading from './loading.svg'
|
|
|
14
16
|
import Search from './search.svg'
|
|
15
17
|
import Show from './show.svg'
|
|
16
18
|
import ShowOff from './show-off.svg'
|
|
19
|
+
import Sorter from './sorter.svg'
|
|
17
20
|
import Warning from './warning.svg'
|
|
18
21
|
|
|
19
22
|
export {
|
|
@@ -23,6 +26,8 @@ export {
|
|
|
23
26
|
AngleRight,
|
|
24
27
|
AngleUp,
|
|
25
28
|
Check,
|
|
29
|
+
CheckboxActive,
|
|
30
|
+
CheckboxInactive,
|
|
26
31
|
Close,
|
|
27
32
|
Done,
|
|
28
33
|
EditColumns,
|
|
@@ -33,5 +38,6 @@ export {
|
|
|
33
38
|
Search,
|
|
34
39
|
Show,
|
|
35
40
|
ShowOff,
|
|
41
|
+
Sorter,
|
|
36
42
|
Warning,
|
|
37
43
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path class="down" d="M15.598 12H4.402c-.335 0-.523.343-.315.578l5.598 6.287c.16.18.468.18.63 0l5.598-6.287c.208-.235.02-.578-.316-.578Z" fill="#444"/><path class="up" d="m15.913 7.422-5.598-6.287a.436.436 0 0 0-.63 0L4.087 7.422c-.208.235-.02.578.316.578h11.195c.335 0 .523-.343.315-.578Z" fill="#444"/></svg>
|
|
@@ -65,3 +65,42 @@ $font-primary: $font-base-stretch $text-base-style $font-base-weight #{$text-bas
|
|
|
65
65
|
font-size: 0.875rem;
|
|
66
66
|
line-height: 0.775rem;
|
|
67
67
|
}
|
|
68
|
+
|
|
69
|
+
@mixin cards-table-list-header {
|
|
70
|
+
font-style: $text-base-style;
|
|
71
|
+
font-variant: $text-base-style;
|
|
72
|
+
font-weight: 700;
|
|
73
|
+
font-family: $font-base-family;
|
|
74
|
+
color: color_alias.$neutral-white;
|
|
75
|
+
font-size: 1rem;
|
|
76
|
+
line-height: 1.375rem; /* 137.5% */
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@mixin cards-table-list-highlight-text {
|
|
80
|
+
font-style: $text-base-style;
|
|
81
|
+
font-variant: $text-base-style;
|
|
82
|
+
font-weight: 700;
|
|
83
|
+
font-family: $font-base-family;
|
|
84
|
+
font-size: 1rem;
|
|
85
|
+
line-height: 1.5rem; /* 150% */
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@mixin cards-table-list-text {
|
|
89
|
+
font-style: $text-base-style;
|
|
90
|
+
font-variant: $text-base-style;
|
|
91
|
+
font-weight: 400;
|
|
92
|
+
font-family: $font-base-family;
|
|
93
|
+
color: color_alias.$neutral-color-1000;
|
|
94
|
+
font-size: 1rem;
|
|
95
|
+
line-height: 1.5rem; /* 150% */
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@mixin cards-table-list-disabled-text {
|
|
99
|
+
font-style: $text-base-style;
|
|
100
|
+
font-variant: $text-base-style;
|
|
101
|
+
font-weight: 400;
|
|
102
|
+
font-family: $font-base-family;
|
|
103
|
+
color: color_alias.$neutral-color-200;
|
|
104
|
+
font-size: 1rem;
|
|
105
|
+
line-height: 1.5rem; /* 150% */
|
|
106
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { CardsTableList } from '../atoms/CardsTableList'
|
|
2
|
+
import { StoryObj } from '@storybook/react'
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Design System/Atoms/Cards Table List',
|
|
6
|
+
component: CardsTableList,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
argTypes: {
|
|
9
|
+
variant: {
|
|
10
|
+
description: 'Select variant used',
|
|
11
|
+
},
|
|
12
|
+
summary: {
|
|
13
|
+
description:
|
|
14
|
+
'Summary of the table purpose and structure for assistive technologies',
|
|
15
|
+
},
|
|
16
|
+
headers: {
|
|
17
|
+
description: 'Array of values to be displayed on the headers',
|
|
18
|
+
},
|
|
19
|
+
rows: {
|
|
20
|
+
description: 'Array of values to be displayed as the data',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const figmaPrimaryDesign = {
|
|
26
|
+
design: {
|
|
27
|
+
type: 'figma',
|
|
28
|
+
url: 'https://www.figma.com/file/DN2ova21vWqCRvPspBXgI1/Design-System?type=design&node-id=1272-1328&mode=dev',
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default meta
|
|
33
|
+
type Story = StoryObj<typeof meta>
|
|
34
|
+
|
|
35
|
+
export const Primary: Story = {
|
|
36
|
+
args: {
|
|
37
|
+
variant: 'primary',
|
|
38
|
+
summary: 'Videogames companies contact information',
|
|
39
|
+
headers: [
|
|
40
|
+
{
|
|
41
|
+
label: 'Game title',
|
|
42
|
+
icon: 'Info',
|
|
43
|
+
},
|
|
44
|
+
{ label: 'Company address' },
|
|
45
|
+
{
|
|
46
|
+
label: 'Customer service email',
|
|
47
|
+
icon: 'Info',
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
rows: [
|
|
51
|
+
{
|
|
52
|
+
id: '1',
|
|
53
|
+
isDisabled: false,
|
|
54
|
+
data: {
|
|
55
|
+
name: 'Metal Gear Solid 5: The Phantom Pain',
|
|
56
|
+
address:
|
|
57
|
+
'Konami Digital Entertainment Co., Ltd. 1-11-1, Ginza, Chuo-ku, Tokyo, 104-0061 Japan',
|
|
58
|
+
email: 'konami@fakemail.com',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: '2',
|
|
63
|
+
isDisabled: false,
|
|
64
|
+
data: {
|
|
65
|
+
name: 'The Witcher 3',
|
|
66
|
+
address: 'CD PROJEKT S.A. ul. Jagiellońska 74 03-301 Warszawa Poland',
|
|
67
|
+
email: 'cdprojekt@fakemail.com',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: '3',
|
|
72
|
+
isDisabled: true,
|
|
73
|
+
data: {
|
|
74
|
+
name: 'Tekken 8',
|
|
75
|
+
address:
|
|
76
|
+
'Bandai Namco Studios Inc. ; Address: 2-37-25 Eitai, Koto-ku, Tokyo 135-0034, Japan',
|
|
77
|
+
email: 'namco@fakemail.com',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
parameters: figmaPrimaryDesign,
|
|
83
|
+
}
|
|
@@ -3,4 +3,37 @@ import { Meta } from "@storybook/addon-docs";
|
|
|
3
3
|
<Meta title="Changelog" />
|
|
4
4
|
# Changelog
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
## 0.1.2
|
|
7
|
+
|
|
8
|
+
Cards Table List component is added to Storybook with the following features:
|
|
9
|
+
|
|
10
|
+
- Headers with icon
|
|
11
|
+
- Cells text with default and bold styles
|
|
12
|
+
- Highlight style for the first cell in a row
|
|
13
|
+
- Row default, hover and disabled styles
|
|
14
|
+
|
|
15
|
+
Changelog has been updated with all published versions.
|
|
16
|
+
|
|
17
|
+
The order of the Docs stories has been updated.
|
|
18
|
+
|
|
19
|
+
## 0.1.1
|
|
20
|
+
|
|
21
|
+
The "required" property has been removed from Input and Select components.
|
|
22
|
+
|
|
23
|
+
The "invalid" property has been created to manage error states.
|
|
24
|
+
|
|
25
|
+
Styles unification has been applied on Select component.
|
|
26
|
+
|
|
27
|
+
## 0.1.0
|
|
28
|
+
|
|
29
|
+
Multiselect component is added to Storybook.
|
|
30
|
+
|
|
31
|
+
Eye icon has been added to Input of type password on Storybook.
|
|
32
|
+
|
|
33
|
+
Welcome and Docs pages have been added.
|
|
34
|
+
|
|
35
|
+
Icon gallery has been added.
|
|
36
|
+
|
|
37
|
+
Select component has been added.
|
|
38
|
+
|
|
39
|
+
Input component has been added.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Multiselect } from '../atoms/Multiselect'
|
|
2
|
+
import { StoryObj } from '@storybook/react'
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Design System/Atoms/Multiselect',
|
|
6
|
+
component: Multiselect,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
argTypes: {
|
|
9
|
+
label: {
|
|
10
|
+
description: 'Label for the select',
|
|
11
|
+
},
|
|
12
|
+
selectedLabel: {
|
|
13
|
+
description: 'Label used when having selected values',
|
|
14
|
+
},
|
|
15
|
+
variant: {
|
|
16
|
+
description: 'Select variant used',
|
|
17
|
+
},
|
|
18
|
+
disabled: {
|
|
19
|
+
description: 'Is the select in disabled state?',
|
|
20
|
+
},
|
|
21
|
+
invalid: {
|
|
22
|
+
description: 'Is the select in disabled state?',
|
|
23
|
+
},
|
|
24
|
+
helpText: {
|
|
25
|
+
description: 'Optional help text',
|
|
26
|
+
},
|
|
27
|
+
name: {
|
|
28
|
+
description: 'Set name property',
|
|
29
|
+
},
|
|
30
|
+
placeholder: {
|
|
31
|
+
description: 'Set select placeholder text',
|
|
32
|
+
},
|
|
33
|
+
options: {
|
|
34
|
+
description: 'Array of values to be displayed on the select list',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const figmaPrimaryDesign = {
|
|
40
|
+
design: {
|
|
41
|
+
type: 'figma',
|
|
42
|
+
url: 'https://www.figma.com/file/DN2ova21vWqCRvPspBXgI1/Design-System?type=design&node-id=454-1657&mode=dev',
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default meta
|
|
47
|
+
type Story = StoryObj<typeof meta>
|
|
48
|
+
|
|
49
|
+
export const Primary: Story = {
|
|
50
|
+
args: {
|
|
51
|
+
variant: 'primary',
|
|
52
|
+
disabled: false,
|
|
53
|
+
invalid: false,
|
|
54
|
+
helpText: 'This text can help you',
|
|
55
|
+
name: 'example',
|
|
56
|
+
label: 'Videogames',
|
|
57
|
+
selectedLabel: 'videogames selected',
|
|
58
|
+
placeholder: 'Select your favourite videogames...',
|
|
59
|
+
options: [
|
|
60
|
+
{ id: '1', label: 'The Legend of Zelda: Ocarina of Time' },
|
|
61
|
+
{ id: '2', label: 'Spyro the Dragon' },
|
|
62
|
+
{ id: '3', label: 'Halo' },
|
|
63
|
+
{ id: '4', label: 'Tetris' },
|
|
64
|
+
{ id: '5', label: 'Super Mario Bros' },
|
|
65
|
+
{ id: '6', label: 'Red Dead Redemption' },
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
parameters: figmaPrimaryDesign,
|
|
69
|
+
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
name: Deploy design system to Chromatic
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
workflow_dispatch:
|
|
5
|
-
|
|
6
|
-
jobs:
|
|
7
|
-
chromatic-deployment:
|
|
8
|
-
runs-on: ubuntu-latest
|
|
9
|
-
|
|
10
|
-
steps:
|
|
11
|
-
- name: Checkout repository
|
|
12
|
-
uses: actions/checkout@v3
|
|
13
|
-
with:
|
|
14
|
-
fetch-depth: 0
|
|
15
|
-
|
|
16
|
-
- name: Install dependencies
|
|
17
|
-
run: npm install --workspace=@wineries/design-system
|
|
18
|
-
|
|
19
|
-
- name: Publish to Chromatic 🚀
|
|
20
|
-
uses: chromaui/action@v1
|
|
21
|
-
|
|
22
|
-
with:
|
|
23
|
-
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
|
24
|
-
buildScriptName: 'design-system:build'
|