@sproutsocial/seeds-react-segmented-control 1.0.0
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/.eslintignore +6 -0
- package/.eslintrc.js +4 -0
- package/.turbo/turbo-build.log +21 -0
- package/CHANGELOG.md +7 -0
- package/dist/esm/index.js +170 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/index.d.mts +32 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +203 -0
- package/dist/index.js.map +1 -0
- package/jest.config.js +9 -0
- package/package.json +45 -0
- package/src/SegmentedControl.stories.tsx +72 -0
- package/src/SegmentedControl.tsx +102 -0
- package/src/SegmentedControlTypes.ts +25 -0
- package/src/__tests__/SegmentedControl.test.tsx +84 -0
- package/src/__tests__/SegmentedControl.typetest.tsx +24 -0
- package/src/index.ts +5 -0
- package/src/styled.d.ts +7 -0
- package/src/styles.ts +93 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +12 -0
package/.eslintignore
ADDED
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
yarn run v1.22.22
|
|
2
|
+
$ tsup --dts
|
|
3
|
+
CLI Building entry: src/index.ts
|
|
4
|
+
CLI Using tsconfig: tsconfig.json
|
|
5
|
+
CLI tsup v8.0.2
|
|
6
|
+
CLI Using tsup config: /home/runner/work/seeds/seeds/seeds-react/seeds-react-segmented-control/tsup.config.ts
|
|
7
|
+
CLI Target: es2022
|
|
8
|
+
CLI Cleaning output folder
|
|
9
|
+
CJS Build start
|
|
10
|
+
ESM Build start
|
|
11
|
+
CJS dist/index.js 6.72 KB
|
|
12
|
+
CJS dist/index.js.map 8.90 KB
|
|
13
|
+
CJS ⚡️ Build success in 129ms
|
|
14
|
+
ESM dist/esm/index.js 4.47 KB
|
|
15
|
+
ESM dist/esm/index.js.map 8.81 KB
|
|
16
|
+
ESM ⚡️ Build success in 134ms
|
|
17
|
+
DTS Build start
|
|
18
|
+
DTS ⚡️ Build success in 23224ms
|
|
19
|
+
DTS dist/index.d.ts 1.47 KB
|
|
20
|
+
DTS dist/index.d.mts 1.47 KB
|
|
21
|
+
Done in 30.91s.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// src/SegmentedControl.tsx
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { useState, useContext } from "react";
|
|
4
|
+
import Box2 from "@sproutsocial/seeds-react-box";
|
|
5
|
+
|
|
6
|
+
// src/styles.ts
|
|
7
|
+
import styled, { css } from "styled-components";
|
|
8
|
+
import {
|
|
9
|
+
visuallyHidden,
|
|
10
|
+
focusRing,
|
|
11
|
+
disabled
|
|
12
|
+
} from "@sproutsocial/seeds-react-mixins";
|
|
13
|
+
import Box from "@sproutsocial/seeds-react-box";
|
|
14
|
+
import Text from "@sproutsocial/seeds-react-text";
|
|
15
|
+
var SegmentedControlContainer = styled(Box)`
|
|
16
|
+
border: 1px solid
|
|
17
|
+
${(props) => props.theme.colors.button.secondary.border.base};
|
|
18
|
+
border-radius: ${(props) => props.theme.radii.outer};
|
|
19
|
+
padding: ${(props) => props.theme.space[100]};
|
|
20
|
+
|
|
21
|
+
legend {
|
|
22
|
+
${visuallyHidden}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
${(props) => props.disabled && disabled}
|
|
26
|
+
`;
|
|
27
|
+
var SegmentedControlItemContainer = styled(Box)`
|
|
28
|
+
flex: 1 1 auto;
|
|
29
|
+
display: flex;
|
|
30
|
+
cursor: pointer;
|
|
31
|
+
|
|
32
|
+
& + & {
|
|
33
|
+
margin-left: ${(props) => props.theme.space[100]};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
&:focus-within label {
|
|
37
|
+
${focusRing}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
input {
|
|
41
|
+
${visuallyHidden}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
${(props) => props.disabled && disabled}
|
|
45
|
+
`;
|
|
46
|
+
var SegmentedControlLabel = styled(Text)`
|
|
47
|
+
flex: 1 1 auto;
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
justify-content: center;
|
|
51
|
+
text-align: center;
|
|
52
|
+
color: ${(props) => props.theme.colors.text.body};
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
font-size: ${(props) => props.theme.typography[200].fontSize};
|
|
55
|
+
/**
|
|
56
|
+
* Matches default line height of Icon. Also matches the overall height of
|
|
57
|
+
* Input, Select, and Button.
|
|
58
|
+
*/
|
|
59
|
+
line-height: 16px;
|
|
60
|
+
font-weight: ${(props) => props.theme.fontWeights.semibold};
|
|
61
|
+
|
|
62
|
+
border-radius: ${(props) => props.theme.radii.inner};
|
|
63
|
+
/* To match the height of buttons... 350 padding - 2px top and bottom padding of the parent - 1px border on top and bottom */
|
|
64
|
+
padding: calc(${(props) => props.theme.space[350]} - 6px);
|
|
65
|
+
transition: all ${(props) => props.theme.duration.fast};
|
|
66
|
+
|
|
67
|
+
&:hover {
|
|
68
|
+
background-color: ${(props) => props.theme.colors.listItem.background.hover};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
${(props) => props.isActive && css`
|
|
72
|
+
color: ${(props2) => props2.theme.colors.text.inverse};
|
|
73
|
+
background-color: ${(props2) => props2.theme.colors.listItem.background.selected};
|
|
74
|
+
|
|
75
|
+
&:hover {
|
|
76
|
+
background-color: ${(props2) => props2.theme.colors.listItem.background.selected};
|
|
77
|
+
}
|
|
78
|
+
`}
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
// src/SegmentedControl.tsx
|
|
82
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
83
|
+
var nameCounter = 0;
|
|
84
|
+
var idCounter = 0;
|
|
85
|
+
var SegmentedControlContext = React.createContext(null);
|
|
86
|
+
var SegmentedControlItem = ({
|
|
87
|
+
value,
|
|
88
|
+
children,
|
|
89
|
+
disabled: disabled2,
|
|
90
|
+
...rest
|
|
91
|
+
}) => {
|
|
92
|
+
const context = useContext(SegmentedControlContext);
|
|
93
|
+
if (!context) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const { name, selectedValue, onChange } = context;
|
|
97
|
+
const [id] = useState(`${name}-${idCounter++}`);
|
|
98
|
+
const isChecked = value === selectedValue;
|
|
99
|
+
return /* @__PURE__ */ jsxs(
|
|
100
|
+
SegmentedControlItemContainer,
|
|
101
|
+
{
|
|
102
|
+
"data-segmentedcontrol-isactive": isChecked,
|
|
103
|
+
"data-qa-segmentedcontrol-item": value,
|
|
104
|
+
"data-qa-segmentedcontrol-ischecked": isChecked === true,
|
|
105
|
+
disabled: disabled2,
|
|
106
|
+
...rest,
|
|
107
|
+
children: [
|
|
108
|
+
/* @__PURE__ */ jsx(
|
|
109
|
+
"input",
|
|
110
|
+
{
|
|
111
|
+
type: "radio",
|
|
112
|
+
id,
|
|
113
|
+
name,
|
|
114
|
+
value,
|
|
115
|
+
checked: isChecked,
|
|
116
|
+
onChange,
|
|
117
|
+
disabled: disabled2
|
|
118
|
+
}
|
|
119
|
+
),
|
|
120
|
+
/* @__PURE__ */ jsx(SegmentedControlLabel, { as: "label", htmlFor: id, isActive: isChecked, children })
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
var SegmentedControl = ({
|
|
126
|
+
selectedValue,
|
|
127
|
+
label,
|
|
128
|
+
onChange,
|
|
129
|
+
children,
|
|
130
|
+
...rest
|
|
131
|
+
}) => {
|
|
132
|
+
const [name] = useState(`Racine-segmented-control-${nameCounter++}`);
|
|
133
|
+
return /* @__PURE__ */ jsx(
|
|
134
|
+
SegmentedControlContext.Provider,
|
|
135
|
+
{
|
|
136
|
+
value: {
|
|
137
|
+
name,
|
|
138
|
+
selectedValue,
|
|
139
|
+
onChange
|
|
140
|
+
},
|
|
141
|
+
children: /* @__PURE__ */ jsxs(
|
|
142
|
+
SegmentedControlContainer,
|
|
143
|
+
{
|
|
144
|
+
forwardedAs: "fieldset",
|
|
145
|
+
"data-qa-segmentedcontrol": label,
|
|
146
|
+
"data-qa-segmentedcontrol-value": selectedValue,
|
|
147
|
+
...rest,
|
|
148
|
+
children: [
|
|
149
|
+
/* @__PURE__ */ jsx("legend", { children: label }),
|
|
150
|
+
/* @__PURE__ */ jsx(Box2, { display: "flex", children })
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
SegmentedControlItem.displayName = "SegmentedControl.Item";
|
|
158
|
+
SegmentedControl.Item = SegmentedControlItem;
|
|
159
|
+
var SegmentedControl_default = SegmentedControl;
|
|
160
|
+
|
|
161
|
+
// src/SegmentedControlTypes.ts
|
|
162
|
+
import "react";
|
|
163
|
+
|
|
164
|
+
// src/index.ts
|
|
165
|
+
var src_default = SegmentedControl_default;
|
|
166
|
+
export {
|
|
167
|
+
SegmentedControl_default as SegmentedControl,
|
|
168
|
+
src_default as default
|
|
169
|
+
};
|
|
170
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/SegmentedControl.tsx","../../src/styles.ts","../../src/SegmentedControlTypes.ts","../../src/index.ts"],"sourcesContent":["import * as React from \"react\";\nimport { useState, useContext } from \"react\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport {\n SegmentedControlContainer,\n SegmentedControlItemContainer,\n SegmentedControlLabel,\n} from \"./styles\";\nimport type {\n TypeSegmentedControlProps,\n TypeSegmentedControlItemProps,\n} from \"./SegmentedControlTypes\";\n\nlet nameCounter = 0;\nlet idCounter = 0;\n\ninterface TypeSegmentedControlContext {\n name: string;\n selectedValue: string;\n onChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;\n}\n\nconst SegmentedControlContext = React.createContext<\n TypeSegmentedControlContext | null | undefined\n>(null);\n\nconst SegmentedControlItem = ({\n value,\n children,\n disabled,\n ...rest\n}: TypeSegmentedControlItemProps) => {\n const context = useContext(SegmentedControlContext);\n\n if (!context) {\n return null;\n }\n\n const { name, selectedValue, onChange } = context;\n\n /* eslint-disable-next-line */\n const [id] = useState(`${name}-${idCounter++}`);\n const isChecked = value === selectedValue;\n\n return (\n <SegmentedControlItemContainer\n data-segmentedcontrol-isactive={isChecked}\n data-qa-segmentedcontrol-item={value}\n data-qa-segmentedcontrol-ischecked={isChecked === true}\n disabled={disabled}\n {...rest}\n >\n <input\n type=\"radio\"\n id={id}\n name={name}\n value={value}\n checked={isChecked}\n onChange={onChange}\n disabled={disabled}\n />\n <SegmentedControlLabel as=\"label\" htmlFor={id} isActive={isChecked}>\n {children}\n </SegmentedControlLabel>\n </SegmentedControlItemContainer>\n );\n};\n\nconst SegmentedControl = ({\n selectedValue,\n label,\n onChange,\n children,\n ...rest\n}: TypeSegmentedControlProps) => {\n const [name] = useState(`Racine-segmented-control-${nameCounter++}`);\n return (\n <SegmentedControlContext.Provider\n value={{\n name,\n selectedValue,\n onChange,\n }}\n >\n <SegmentedControlContainer\n forwardedAs=\"fieldset\"\n data-qa-segmentedcontrol={label}\n data-qa-segmentedcontrol-value={selectedValue}\n {...rest}\n >\n <legend>{label}</legend>\n\n <Box display=\"flex\">{children}</Box>\n </SegmentedControlContainer>\n </SegmentedControlContext.Provider>\n );\n};\n\nSegmentedControlItem.displayName = \"SegmentedControl.Item\";\nSegmentedControl.Item = SegmentedControlItem;\n\nexport default SegmentedControl;\n","import styled, { css } from \"styled-components\";\nimport {\n visuallyHidden,\n focusRing,\n disabled,\n} from \"@sproutsocial/seeds-react-mixins\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport type {\n TypeSegmentedControlProps,\n TypeSegmentedControlItemProps,\n} from \"./SegmentedControlTypes\";\n\nexport const SegmentedControlContainer = styled(Box)<\n Pick<TypeSegmentedControlProps, \"disabled\">\n>`\n border: 1px solid\n ${(props) => props.theme.colors.button.secondary.border.base};\n border-radius: ${(props) => props.theme.radii.outer};\n padding: ${(props) => props.theme.space[100]};\n\n legend {\n ${visuallyHidden}\n }\n\n ${(props) => props.disabled && disabled}\n`;\n\nexport const SegmentedControlItemContainer = styled(Box)<\n Pick<TypeSegmentedControlItemProps, \"disabled\">\n>`\n flex: 1 1 auto;\n display: flex;\n cursor: pointer;\n\n & + & {\n margin-left: ${(props) => props.theme.space[100]};\n }\n\n &:focus-within label {\n ${focusRing}\n }\n\n input {\n ${visuallyHidden}\n }\n\n ${(props) => props.disabled && disabled}\n`;\n\ninterface TypeSegmentedControlState {\n isActive: boolean;\n}\n\nexport const SegmentedControlLabel = styled(Text)<TypeSegmentedControlState>`\n flex: 1 1 auto;\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n color: ${(props) => props.theme.colors.text.body};\n cursor: pointer;\n font-size: ${(props) => props.theme.typography[200].fontSize};\n /**\n\t* Matches default line height of Icon. Also matches the overall height of\n\t* Input, Select, and Button.\n\t*/\n line-height: 16px;\n font-weight: ${(props) => props.theme.fontWeights.semibold};\n\n border-radius: ${(props) => props.theme.radii.inner};\n /* To match the height of buttons... 350 padding - 2px top and bottom padding of the parent - 1px border on top and bottom */\n padding: calc(${(props) => props.theme.space[350]} - 6px);\n transition: all ${(props) => props.theme.duration.fast};\n\n &:hover {\n background-color: ${(props) =>\n props.theme.colors.listItem.background.hover};\n }\n\n ${(props) =>\n props.isActive &&\n css`\n color: ${(props) => props.theme.colors.text.inverse};\n background-color: ${(props) =>\n props.theme.colors.listItem.background.selected};\n\n &:hover {\n background-color: ${(props) =>\n props.theme.colors.listItem.background.selected};\n }\n `}\n`;\n","import type { TypeContainerProps } from \"@sproutsocial/seeds-react-box\";\nimport * as React from \"react\";\n\nexport interface TypeSegmentedControlItemProps {\n /** The value of this item. Should be unique among sibling items. */\n value: string;\n children: React.ReactNode;\n /** Disables user action and applies a disabled style on the component */\n disabled?: boolean;\n}\n\nexport interface TypeSegmentedControlProps extends TypeContainerProps {\n /** The value of the currently selected item. Should match the value prop of one of the child items */\n selectedValue: string;\n\n /** The title of the segmented control, used for accessibility purposes */\n label: string;\n\n /** Called when the user selects a new item. You can access the value of the newly selected item using \"event.target.value\" */\n onChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;\n\n /** Disables user action and applies a disabled style on the component */\n disabled?: boolean;\n children: React.ReactNode;\n}\n","import SegmentedControl from \"./SegmentedControl\";\n\nexport default SegmentedControl;\nexport { SegmentedControl };\nexport * from \"./SegmentedControlTypes\";\n"],"mappings":"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","names":["Box","props","disabled","Box"]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { TypeContainerProps } from '@sproutsocial/seeds-react-box';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
interface TypeSegmentedControlItemProps {
|
|
6
|
+
/** The value of this item. Should be unique among sibling items. */
|
|
7
|
+
value: string;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
/** Disables user action and applies a disabled style on the component */
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface TypeSegmentedControlProps extends TypeContainerProps {
|
|
13
|
+
/** The value of the currently selected item. Should match the value prop of one of the child items */
|
|
14
|
+
selectedValue: string;
|
|
15
|
+
/** The title of the segmented control, used for accessibility purposes */
|
|
16
|
+
label: string;
|
|
17
|
+
/** Called when the user selects a new item. You can access the value of the newly selected item using "event.target.value" */
|
|
18
|
+
onChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;
|
|
19
|
+
/** Disables user action and applies a disabled style on the component */
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
children: React.ReactNode;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare const SegmentedControl: {
|
|
25
|
+
({ selectedValue, label, onChange, children, ...rest }: TypeSegmentedControlProps): react_jsx_runtime.JSX.Element;
|
|
26
|
+
Item: {
|
|
27
|
+
({ value, children, disabled, ...rest }: TypeSegmentedControlItemProps): react_jsx_runtime.JSX.Element | null;
|
|
28
|
+
displayName: string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export { SegmentedControl, type TypeSegmentedControlItemProps, type TypeSegmentedControlProps, SegmentedControl as default };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { TypeContainerProps } from '@sproutsocial/seeds-react-box';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
interface TypeSegmentedControlItemProps {
|
|
6
|
+
/** The value of this item. Should be unique among sibling items. */
|
|
7
|
+
value: string;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
/** Disables user action and applies a disabled style on the component */
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface TypeSegmentedControlProps extends TypeContainerProps {
|
|
13
|
+
/** The value of the currently selected item. Should match the value prop of one of the child items */
|
|
14
|
+
selectedValue: string;
|
|
15
|
+
/** The title of the segmented control, used for accessibility purposes */
|
|
16
|
+
label: string;
|
|
17
|
+
/** Called when the user selects a new item. You can access the value of the newly selected item using "event.target.value" */
|
|
18
|
+
onChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;
|
|
19
|
+
/** Disables user action and applies a disabled style on the component */
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
children: React.ReactNode;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare const SegmentedControl: {
|
|
25
|
+
({ selectedValue, label, onChange, children, ...rest }: TypeSegmentedControlProps): react_jsx_runtime.JSX.Element;
|
|
26
|
+
Item: {
|
|
27
|
+
({ value, children, disabled, ...rest }: TypeSegmentedControlItemProps): react_jsx_runtime.JSX.Element | null;
|
|
28
|
+
displayName: string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export { SegmentedControl, type TypeSegmentedControlItemProps, type TypeSegmentedControlProps, SegmentedControl as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
SegmentedControl: () => SegmentedControl_default,
|
|
34
|
+
default: () => src_default
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(src_exports);
|
|
37
|
+
|
|
38
|
+
// src/SegmentedControl.tsx
|
|
39
|
+
var React = __toESM(require("react"));
|
|
40
|
+
var import_react = require("react");
|
|
41
|
+
var import_seeds_react_box2 = __toESM(require("@sproutsocial/seeds-react-box"));
|
|
42
|
+
|
|
43
|
+
// src/styles.ts
|
|
44
|
+
var import_styled_components = __toESM(require("styled-components"));
|
|
45
|
+
var import_seeds_react_mixins = require("@sproutsocial/seeds-react-mixins");
|
|
46
|
+
var import_seeds_react_box = __toESM(require("@sproutsocial/seeds-react-box"));
|
|
47
|
+
var import_seeds_react_text = __toESM(require("@sproutsocial/seeds-react-text"));
|
|
48
|
+
var SegmentedControlContainer = (0, import_styled_components.default)(import_seeds_react_box.default)`
|
|
49
|
+
border: 1px solid
|
|
50
|
+
${(props) => props.theme.colors.button.secondary.border.base};
|
|
51
|
+
border-radius: ${(props) => props.theme.radii.outer};
|
|
52
|
+
padding: ${(props) => props.theme.space[100]};
|
|
53
|
+
|
|
54
|
+
legend {
|
|
55
|
+
${import_seeds_react_mixins.visuallyHidden}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
${(props) => props.disabled && import_seeds_react_mixins.disabled}
|
|
59
|
+
`;
|
|
60
|
+
var SegmentedControlItemContainer = (0, import_styled_components.default)(import_seeds_react_box.default)`
|
|
61
|
+
flex: 1 1 auto;
|
|
62
|
+
display: flex;
|
|
63
|
+
cursor: pointer;
|
|
64
|
+
|
|
65
|
+
& + & {
|
|
66
|
+
margin-left: ${(props) => props.theme.space[100]};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
&:focus-within label {
|
|
70
|
+
${import_seeds_react_mixins.focusRing}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
input {
|
|
74
|
+
${import_seeds_react_mixins.visuallyHidden}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
${(props) => props.disabled && import_seeds_react_mixins.disabled}
|
|
78
|
+
`;
|
|
79
|
+
var SegmentedControlLabel = (0, import_styled_components.default)(import_seeds_react_text.default)`
|
|
80
|
+
flex: 1 1 auto;
|
|
81
|
+
display: flex;
|
|
82
|
+
align-items: center;
|
|
83
|
+
justify-content: center;
|
|
84
|
+
text-align: center;
|
|
85
|
+
color: ${(props) => props.theme.colors.text.body};
|
|
86
|
+
cursor: pointer;
|
|
87
|
+
font-size: ${(props) => props.theme.typography[200].fontSize};
|
|
88
|
+
/**
|
|
89
|
+
* Matches default line height of Icon. Also matches the overall height of
|
|
90
|
+
* Input, Select, and Button.
|
|
91
|
+
*/
|
|
92
|
+
line-height: 16px;
|
|
93
|
+
font-weight: ${(props) => props.theme.fontWeights.semibold};
|
|
94
|
+
|
|
95
|
+
border-radius: ${(props) => props.theme.radii.inner};
|
|
96
|
+
/* To match the height of buttons... 350 padding - 2px top and bottom padding of the parent - 1px border on top and bottom */
|
|
97
|
+
padding: calc(${(props) => props.theme.space[350]} - 6px);
|
|
98
|
+
transition: all ${(props) => props.theme.duration.fast};
|
|
99
|
+
|
|
100
|
+
&:hover {
|
|
101
|
+
background-color: ${(props) => props.theme.colors.listItem.background.hover};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
${(props) => props.isActive && import_styled_components.css`
|
|
105
|
+
color: ${(props2) => props2.theme.colors.text.inverse};
|
|
106
|
+
background-color: ${(props2) => props2.theme.colors.listItem.background.selected};
|
|
107
|
+
|
|
108
|
+
&:hover {
|
|
109
|
+
background-color: ${(props2) => props2.theme.colors.listItem.background.selected};
|
|
110
|
+
}
|
|
111
|
+
`}
|
|
112
|
+
`;
|
|
113
|
+
|
|
114
|
+
// src/SegmentedControl.tsx
|
|
115
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
116
|
+
var nameCounter = 0;
|
|
117
|
+
var idCounter = 0;
|
|
118
|
+
var SegmentedControlContext = React.createContext(null);
|
|
119
|
+
var SegmentedControlItem = ({
|
|
120
|
+
value,
|
|
121
|
+
children,
|
|
122
|
+
disabled: disabled2,
|
|
123
|
+
...rest
|
|
124
|
+
}) => {
|
|
125
|
+
const context = (0, import_react.useContext)(SegmentedControlContext);
|
|
126
|
+
if (!context) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
const { name, selectedValue, onChange } = context;
|
|
130
|
+
const [id] = (0, import_react.useState)(`${name}-${idCounter++}`);
|
|
131
|
+
const isChecked = value === selectedValue;
|
|
132
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
133
|
+
SegmentedControlItemContainer,
|
|
134
|
+
{
|
|
135
|
+
"data-segmentedcontrol-isactive": isChecked,
|
|
136
|
+
"data-qa-segmentedcontrol-item": value,
|
|
137
|
+
"data-qa-segmentedcontrol-ischecked": isChecked === true,
|
|
138
|
+
disabled: disabled2,
|
|
139
|
+
...rest,
|
|
140
|
+
children: [
|
|
141
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
142
|
+
"input",
|
|
143
|
+
{
|
|
144
|
+
type: "radio",
|
|
145
|
+
id,
|
|
146
|
+
name,
|
|
147
|
+
value,
|
|
148
|
+
checked: isChecked,
|
|
149
|
+
onChange,
|
|
150
|
+
disabled: disabled2
|
|
151
|
+
}
|
|
152
|
+
),
|
|
153
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SegmentedControlLabel, { as: "label", htmlFor: id, isActive: isChecked, children })
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
var SegmentedControl = ({
|
|
159
|
+
selectedValue,
|
|
160
|
+
label,
|
|
161
|
+
onChange,
|
|
162
|
+
children,
|
|
163
|
+
...rest
|
|
164
|
+
}) => {
|
|
165
|
+
const [name] = (0, import_react.useState)(`Racine-segmented-control-${nameCounter++}`);
|
|
166
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
167
|
+
SegmentedControlContext.Provider,
|
|
168
|
+
{
|
|
169
|
+
value: {
|
|
170
|
+
name,
|
|
171
|
+
selectedValue,
|
|
172
|
+
onChange
|
|
173
|
+
},
|
|
174
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
175
|
+
SegmentedControlContainer,
|
|
176
|
+
{
|
|
177
|
+
forwardedAs: "fieldset",
|
|
178
|
+
"data-qa-segmentedcontrol": label,
|
|
179
|
+
"data-qa-segmentedcontrol-value": selectedValue,
|
|
180
|
+
...rest,
|
|
181
|
+
children: [
|
|
182
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("legend", { children: label }),
|
|
183
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_box2.default, { display: "flex", children })
|
|
184
|
+
]
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
};
|
|
190
|
+
SegmentedControlItem.displayName = "SegmentedControl.Item";
|
|
191
|
+
SegmentedControl.Item = SegmentedControlItem;
|
|
192
|
+
var SegmentedControl_default = SegmentedControl;
|
|
193
|
+
|
|
194
|
+
// src/SegmentedControlTypes.ts
|
|
195
|
+
var React2 = require("react");
|
|
196
|
+
|
|
197
|
+
// src/index.ts
|
|
198
|
+
var src_default = SegmentedControl_default;
|
|
199
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
200
|
+
0 && (module.exports = {
|
|
201
|
+
SegmentedControl
|
|
202
|
+
});
|
|
203
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/SegmentedControl.tsx","../src/styles.ts","../src/SegmentedControlTypes.ts"],"sourcesContent":["import SegmentedControl from \"./SegmentedControl\";\n\nexport default SegmentedControl;\nexport { SegmentedControl };\nexport * from \"./SegmentedControlTypes\";\n","import * as React from \"react\";\nimport { useState, useContext } from \"react\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport {\n SegmentedControlContainer,\n SegmentedControlItemContainer,\n SegmentedControlLabel,\n} from \"./styles\";\nimport type {\n TypeSegmentedControlProps,\n TypeSegmentedControlItemProps,\n} from \"./SegmentedControlTypes\";\n\nlet nameCounter = 0;\nlet idCounter = 0;\n\ninterface TypeSegmentedControlContext {\n name: string;\n selectedValue: string;\n onChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;\n}\n\nconst SegmentedControlContext = React.createContext<\n TypeSegmentedControlContext | null | undefined\n>(null);\n\nconst SegmentedControlItem = ({\n value,\n children,\n disabled,\n ...rest\n}: TypeSegmentedControlItemProps) => {\n const context = useContext(SegmentedControlContext);\n\n if (!context) {\n return null;\n }\n\n const { name, selectedValue, onChange } = context;\n\n /* eslint-disable-next-line */\n const [id] = useState(`${name}-${idCounter++}`);\n const isChecked = value === selectedValue;\n\n return (\n <SegmentedControlItemContainer\n data-segmentedcontrol-isactive={isChecked}\n data-qa-segmentedcontrol-item={value}\n data-qa-segmentedcontrol-ischecked={isChecked === true}\n disabled={disabled}\n {...rest}\n >\n <input\n type=\"radio\"\n id={id}\n name={name}\n value={value}\n checked={isChecked}\n onChange={onChange}\n disabled={disabled}\n />\n <SegmentedControlLabel as=\"label\" htmlFor={id} isActive={isChecked}>\n {children}\n </SegmentedControlLabel>\n </SegmentedControlItemContainer>\n );\n};\n\nconst SegmentedControl = ({\n selectedValue,\n label,\n onChange,\n children,\n ...rest\n}: TypeSegmentedControlProps) => {\n const [name] = useState(`Racine-segmented-control-${nameCounter++}`);\n return (\n <SegmentedControlContext.Provider\n value={{\n name,\n selectedValue,\n onChange,\n }}\n >\n <SegmentedControlContainer\n forwardedAs=\"fieldset\"\n data-qa-segmentedcontrol={label}\n data-qa-segmentedcontrol-value={selectedValue}\n {...rest}\n >\n <legend>{label}</legend>\n\n <Box display=\"flex\">{children}</Box>\n </SegmentedControlContainer>\n </SegmentedControlContext.Provider>\n );\n};\n\nSegmentedControlItem.displayName = \"SegmentedControl.Item\";\nSegmentedControl.Item = SegmentedControlItem;\n\nexport default SegmentedControl;\n","import styled, { css } from \"styled-components\";\nimport {\n visuallyHidden,\n focusRing,\n disabled,\n} from \"@sproutsocial/seeds-react-mixins\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport type {\n TypeSegmentedControlProps,\n TypeSegmentedControlItemProps,\n} from \"./SegmentedControlTypes\";\n\nexport const SegmentedControlContainer = styled(Box)<\n Pick<TypeSegmentedControlProps, \"disabled\">\n>`\n border: 1px solid\n ${(props) => props.theme.colors.button.secondary.border.base};\n border-radius: ${(props) => props.theme.radii.outer};\n padding: ${(props) => props.theme.space[100]};\n\n legend {\n ${visuallyHidden}\n }\n\n ${(props) => props.disabled && disabled}\n`;\n\nexport const SegmentedControlItemContainer = styled(Box)<\n Pick<TypeSegmentedControlItemProps, \"disabled\">\n>`\n flex: 1 1 auto;\n display: flex;\n cursor: pointer;\n\n & + & {\n margin-left: ${(props) => props.theme.space[100]};\n }\n\n &:focus-within label {\n ${focusRing}\n }\n\n input {\n ${visuallyHidden}\n }\n\n ${(props) => props.disabled && disabled}\n`;\n\ninterface TypeSegmentedControlState {\n isActive: boolean;\n}\n\nexport const SegmentedControlLabel = styled(Text)<TypeSegmentedControlState>`\n flex: 1 1 auto;\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n color: ${(props) => props.theme.colors.text.body};\n cursor: pointer;\n font-size: ${(props) => props.theme.typography[200].fontSize};\n /**\n\t* Matches default line height of Icon. Also matches the overall height of\n\t* Input, Select, and Button.\n\t*/\n line-height: 16px;\n font-weight: ${(props) => props.theme.fontWeights.semibold};\n\n border-radius: ${(props) => props.theme.radii.inner};\n /* To match the height of buttons... 350 padding - 2px top and bottom padding of the parent - 1px border on top and bottom */\n padding: calc(${(props) => props.theme.space[350]} - 6px);\n transition: all ${(props) => props.theme.duration.fast};\n\n &:hover {\n background-color: ${(props) =>\n props.theme.colors.listItem.background.hover};\n }\n\n ${(props) =>\n props.isActive &&\n css`\n color: ${(props) => props.theme.colors.text.inverse};\n background-color: ${(props) =>\n props.theme.colors.listItem.background.selected};\n\n &:hover {\n background-color: ${(props) =>\n props.theme.colors.listItem.background.selected};\n }\n `}\n`;\n","import type { TypeContainerProps } from \"@sproutsocial/seeds-react-box\";\nimport * as React from \"react\";\n\nexport interface TypeSegmentedControlItemProps {\n /** The value of this item. Should be unique among sibling items. */\n value: string;\n children: React.ReactNode;\n /** Disables user action and applies a disabled style on the component */\n disabled?: boolean;\n}\n\nexport interface TypeSegmentedControlProps extends TypeContainerProps {\n /** The value of the currently selected item. Should match the value prop of one of the child items */\n selectedValue: string;\n\n /** The title of the segmented control, used for accessibility purposes */\n label: string;\n\n /** Called when the user selects a new item. You can access the value of the newly selected item using \"event.target.value\" */\n onChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;\n\n /** Disables user action and applies a disabled style on the component */\n disabled?: boolean;\n children: React.ReactNode;\n}\n"],"mappings":"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","names":["import_seeds_react_box","styled","Box","Text","props","disabled","Box","React"]}
|
package/jest.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sproutsocial/seeds-react-segmented-control",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Seeds React SegmentedControl",
|
|
5
|
+
"author": "Sprout Social, Inc.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"module": "dist/esm/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsup --dts",
|
|
12
|
+
"build:debug": "tsup --dts --metafile",
|
|
13
|
+
"dev": "tsup --watch --dts",
|
|
14
|
+
"clean": "rm -rf .turbo dist",
|
|
15
|
+
"clean:modules": "rm -rf node_modules",
|
|
16
|
+
"typecheck": "tsc --noEmit",
|
|
17
|
+
"test": "jest",
|
|
18
|
+
"test:watch": "jest --watch --coverage=false"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@sproutsocial/seeds-react-theme": "*",
|
|
22
|
+
"@sproutsocial/seeds-react-system-props": "*",
|
|
23
|
+
"@sproutsocial/seeds-react-box": "*",
|
|
24
|
+
"@sproutsocial/seeds-react-text": "*",
|
|
25
|
+
"@sproutsocial/seeds-react-mixins": "*"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/react": "^18.0.0",
|
|
29
|
+
"@types/styled-components": "^5.1.26",
|
|
30
|
+
"@sproutsocial/eslint-config-seeds": "*",
|
|
31
|
+
"react": "^18.0.0",
|
|
32
|
+
"styled-components": "^5.2.3",
|
|
33
|
+
"tsup": "^8.0.2",
|
|
34
|
+
"typescript": "^5.6.2",
|
|
35
|
+
"@sproutsocial/seeds-tsconfig": "*",
|
|
36
|
+
"@sproutsocial/seeds-testing": "*",
|
|
37
|
+
"@sproutsocial/seeds-react-testing-library": "*"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"styled-components": "^5.2.3"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=18"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import Box from "@sproutsocial/seeds-react-box";
|
|
4
|
+
import Button from "@sproutsocial/seeds-react-button";
|
|
5
|
+
import SegmentedControl from "./SegmentedControl";
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof SegmentedControl> = {
|
|
8
|
+
title: "Components/SegmentedControl",
|
|
9
|
+
component: SegmentedControl,
|
|
10
|
+
};
|
|
11
|
+
export default meta;
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj<typeof SegmentedControl>;
|
|
14
|
+
|
|
15
|
+
// @ts-ignore IDK what props should be
|
|
16
|
+
const StatefulSegmentedControl = (props) => {
|
|
17
|
+
const [value, setValue] = useState("1");
|
|
18
|
+
return (
|
|
19
|
+
<SegmentedControl
|
|
20
|
+
selectedValue={value}
|
|
21
|
+
label="Segmented control component"
|
|
22
|
+
onChange={(e) => setValue((e.target as HTMLInputElement).value)}
|
|
23
|
+
{...props}
|
|
24
|
+
>
|
|
25
|
+
<SegmentedControl.Item value="1">Test 1</SegmentedControl.Item>
|
|
26
|
+
<SegmentedControl.Item value="2">Test 2</SegmentedControl.Item>
|
|
27
|
+
<SegmentedControl.Item value="3">Test 3</SegmentedControl.Item>
|
|
28
|
+
</SegmentedControl>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const Default: Story = {
|
|
33
|
+
render: () => (
|
|
34
|
+
<Box width={1 / 3} display="flex" alignItems="center" minWidth="25%">
|
|
35
|
+
<StatefulSegmentedControl flex="1" mr={300} />
|
|
36
|
+
<Button appearance="secondary">Some button</Button>
|
|
37
|
+
</Box>
|
|
38
|
+
),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const Disabled: Story = {
|
|
42
|
+
render: () => (
|
|
43
|
+
<Box width={1 / 3} display="flex" alignItems="center" minWidth="25%">
|
|
44
|
+
<StatefulSegmentedControl flex="1" mr={300} disabled={true} />
|
|
45
|
+
<Button appearance="secondary">Some button</Button>
|
|
46
|
+
</Box>
|
|
47
|
+
),
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const DisabledItem: Story = {
|
|
51
|
+
render: () => {
|
|
52
|
+
const [value, setValue] = useState("1");
|
|
53
|
+
return (
|
|
54
|
+
<Box width={1 / 3} display="flex" alignItems="center" minWidth="25%">
|
|
55
|
+
<SegmentedControl
|
|
56
|
+
selectedValue={value}
|
|
57
|
+
label="Segmented control component"
|
|
58
|
+
onChange={(e) => setValue((e.target as HTMLInputElement).value)}
|
|
59
|
+
flex="1"
|
|
60
|
+
>
|
|
61
|
+
<SegmentedControl.Item value="1">Test 1</SegmentedControl.Item>
|
|
62
|
+
<SegmentedControl.Item value="2" disabled={true}>
|
|
63
|
+
Test 2
|
|
64
|
+
</SegmentedControl.Item>
|
|
65
|
+
<SegmentedControl.Item value="3">Test 3</SegmentedControl.Item>
|
|
66
|
+
<SegmentedControl.Item value="4">Test 4</SegmentedControl.Item>
|
|
67
|
+
<SegmentedControl.Item value="5">Test 5</SegmentedControl.Item>
|
|
68
|
+
</SegmentedControl>
|
|
69
|
+
</Box>
|
|
70
|
+
);
|
|
71
|
+
},
|
|
72
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { useState, useContext } from "react";
|
|
3
|
+
import Box from "@sproutsocial/seeds-react-box";
|
|
4
|
+
import {
|
|
5
|
+
SegmentedControlContainer,
|
|
6
|
+
SegmentedControlItemContainer,
|
|
7
|
+
SegmentedControlLabel,
|
|
8
|
+
} from "./styles";
|
|
9
|
+
import type {
|
|
10
|
+
TypeSegmentedControlProps,
|
|
11
|
+
TypeSegmentedControlItemProps,
|
|
12
|
+
} from "./SegmentedControlTypes";
|
|
13
|
+
|
|
14
|
+
let nameCounter = 0;
|
|
15
|
+
let idCounter = 0;
|
|
16
|
+
|
|
17
|
+
interface TypeSegmentedControlContext {
|
|
18
|
+
name: string;
|
|
19
|
+
selectedValue: string;
|
|
20
|
+
onChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const SegmentedControlContext = React.createContext<
|
|
24
|
+
TypeSegmentedControlContext | null | undefined
|
|
25
|
+
>(null);
|
|
26
|
+
|
|
27
|
+
const SegmentedControlItem = ({
|
|
28
|
+
value,
|
|
29
|
+
children,
|
|
30
|
+
disabled,
|
|
31
|
+
...rest
|
|
32
|
+
}: TypeSegmentedControlItemProps) => {
|
|
33
|
+
const context = useContext(SegmentedControlContext);
|
|
34
|
+
|
|
35
|
+
if (!context) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const { name, selectedValue, onChange } = context;
|
|
40
|
+
|
|
41
|
+
/* eslint-disable-next-line */
|
|
42
|
+
const [id] = useState(`${name}-${idCounter++}`);
|
|
43
|
+
const isChecked = value === selectedValue;
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<SegmentedControlItemContainer
|
|
47
|
+
data-segmentedcontrol-isactive={isChecked}
|
|
48
|
+
data-qa-segmentedcontrol-item={value}
|
|
49
|
+
data-qa-segmentedcontrol-ischecked={isChecked === true}
|
|
50
|
+
disabled={disabled}
|
|
51
|
+
{...rest}
|
|
52
|
+
>
|
|
53
|
+
<input
|
|
54
|
+
type="radio"
|
|
55
|
+
id={id}
|
|
56
|
+
name={name}
|
|
57
|
+
value={value}
|
|
58
|
+
checked={isChecked}
|
|
59
|
+
onChange={onChange}
|
|
60
|
+
disabled={disabled}
|
|
61
|
+
/>
|
|
62
|
+
<SegmentedControlLabel as="label" htmlFor={id} isActive={isChecked}>
|
|
63
|
+
{children}
|
|
64
|
+
</SegmentedControlLabel>
|
|
65
|
+
</SegmentedControlItemContainer>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const SegmentedControl = ({
|
|
70
|
+
selectedValue,
|
|
71
|
+
label,
|
|
72
|
+
onChange,
|
|
73
|
+
children,
|
|
74
|
+
...rest
|
|
75
|
+
}: TypeSegmentedControlProps) => {
|
|
76
|
+
const [name] = useState(`Racine-segmented-control-${nameCounter++}`);
|
|
77
|
+
return (
|
|
78
|
+
<SegmentedControlContext.Provider
|
|
79
|
+
value={{
|
|
80
|
+
name,
|
|
81
|
+
selectedValue,
|
|
82
|
+
onChange,
|
|
83
|
+
}}
|
|
84
|
+
>
|
|
85
|
+
<SegmentedControlContainer
|
|
86
|
+
forwardedAs="fieldset"
|
|
87
|
+
data-qa-segmentedcontrol={label}
|
|
88
|
+
data-qa-segmentedcontrol-value={selectedValue}
|
|
89
|
+
{...rest}
|
|
90
|
+
>
|
|
91
|
+
<legend>{label}</legend>
|
|
92
|
+
|
|
93
|
+
<Box display="flex">{children}</Box>
|
|
94
|
+
</SegmentedControlContainer>
|
|
95
|
+
</SegmentedControlContext.Provider>
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
SegmentedControlItem.displayName = "SegmentedControl.Item";
|
|
100
|
+
SegmentedControl.Item = SegmentedControlItem;
|
|
101
|
+
|
|
102
|
+
export default SegmentedControl;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { TypeContainerProps } from "@sproutsocial/seeds-react-box";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
export interface TypeSegmentedControlItemProps {
|
|
5
|
+
/** The value of this item. Should be unique among sibling items. */
|
|
6
|
+
value: string;
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
/** Disables user action and applies a disabled style on the component */
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface TypeSegmentedControlProps extends TypeContainerProps {
|
|
13
|
+
/** The value of the currently selected item. Should match the value prop of one of the child items */
|
|
14
|
+
selectedValue: string;
|
|
15
|
+
|
|
16
|
+
/** The title of the segmented control, used for accessibility purposes */
|
|
17
|
+
label: string;
|
|
18
|
+
|
|
19
|
+
/** Called when the user selects a new item. You can access the value of the newly selected item using "event.target.value" */
|
|
20
|
+
onChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;
|
|
21
|
+
|
|
22
|
+
/** Disables user action and applies a disabled style on the component */
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
children: React.ReactNode;
|
|
25
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
render,
|
|
4
|
+
fireEvent,
|
|
5
|
+
screen,
|
|
6
|
+
} from "@sproutsocial/seeds-react-testing-library";
|
|
7
|
+
import SegmentedControl from "../SegmentedControl";
|
|
8
|
+
|
|
9
|
+
// @ts-ignore IDK what props should be
|
|
10
|
+
const StatefulSegmentedControl = (props) => {
|
|
11
|
+
const [value, setValue] = useState("1");
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<SegmentedControl
|
|
15
|
+
selectedValue={value}
|
|
16
|
+
label="Segmented control component"
|
|
17
|
+
onChange={(e) => setValue((e.target as HTMLInputElement).value)}
|
|
18
|
+
{...props}
|
|
19
|
+
>
|
|
20
|
+
<SegmentedControl.Item value="1">Test 1</SegmentedControl.Item>
|
|
21
|
+
<SegmentedControl.Item value="2">Test 2</SegmentedControl.Item>
|
|
22
|
+
<SegmentedControl.Item value="3">Test 3</SegmentedControl.Item>
|
|
23
|
+
</SegmentedControl>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
describe("SegmentedControl", () => {
|
|
28
|
+
it("should select the correct item when clicked", async () => {
|
|
29
|
+
render(<StatefulSegmentedControl />);
|
|
30
|
+
|
|
31
|
+
const item1 = screen.getByText("Test 1");
|
|
32
|
+
const item2 = screen.getByText("Test 2");
|
|
33
|
+
const item3 = screen.getByText("Test 3");
|
|
34
|
+
|
|
35
|
+
expect(item1?.parentNode).toHaveAttribute(
|
|
36
|
+
"data-segmentedcontrol-isactive",
|
|
37
|
+
"true"
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
expect(item2?.parentNode).toHaveAttribute(
|
|
41
|
+
"data-segmentedcontrol-isactive",
|
|
42
|
+
"false"
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
expect(item3?.parentNode).toHaveAttribute(
|
|
46
|
+
"data-segmentedcontrol-isactive",
|
|
47
|
+
"false"
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
fireEvent.click(item3);
|
|
51
|
+
|
|
52
|
+
expect(item1?.parentNode).toHaveAttribute(
|
|
53
|
+
"data-segmentedcontrol-isactive",
|
|
54
|
+
"false"
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
expect(item2?.parentNode).toHaveAttribute(
|
|
58
|
+
"data-segmentedcontrol-isactive",
|
|
59
|
+
"false"
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
expect(item3?.parentNode).toHaveAttribute(
|
|
63
|
+
"data-segmentedcontrol-isactive",
|
|
64
|
+
"true"
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
fireEvent.click(item2);
|
|
68
|
+
|
|
69
|
+
expect(item1?.parentNode).toHaveAttribute(
|
|
70
|
+
"data-segmentedcontrol-isactive",
|
|
71
|
+
"false"
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
expect(item2?.parentNode).toHaveAttribute(
|
|
75
|
+
"data-segmentedcontrol-isactive",
|
|
76
|
+
"true"
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(item3?.parentNode).toHaveAttribute(
|
|
80
|
+
"data-segmentedcontrol-isactive",
|
|
81
|
+
"false"
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import SegmentedControl from "../SegmentedControl";
|
|
3
|
+
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5
|
+
function SegmentedControlTypes() {
|
|
6
|
+
const mockChange = jest.fn();
|
|
7
|
+
return (
|
|
8
|
+
<>
|
|
9
|
+
<SegmentedControl
|
|
10
|
+
selectedValue="orange"
|
|
11
|
+
label="Fruits"
|
|
12
|
+
onChange={mockChange}
|
|
13
|
+
height="34px"
|
|
14
|
+
ml="300"
|
|
15
|
+
>
|
|
16
|
+
<SegmentedControl.Item value="apple">Apple</SegmentedControl.Item>
|
|
17
|
+
<SegmentedControl.Item value="orange">Orange</SegmentedControl.Item>
|
|
18
|
+
<SegmentedControl.Item value="banana">Banana</SegmentedControl.Item>
|
|
19
|
+
</SegmentedControl>
|
|
20
|
+
{/* @ts-expect-error - test that invalid and missing props are rejected */}
|
|
21
|
+
<SegmentedControl />
|
|
22
|
+
</>
|
|
23
|
+
);
|
|
24
|
+
}
|
package/src/index.ts
ADDED
package/src/styled.d.ts
ADDED
package/src/styles.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import styled, { css } from "styled-components";
|
|
2
|
+
import {
|
|
3
|
+
visuallyHidden,
|
|
4
|
+
focusRing,
|
|
5
|
+
disabled,
|
|
6
|
+
} from "@sproutsocial/seeds-react-mixins";
|
|
7
|
+
import Box from "@sproutsocial/seeds-react-box";
|
|
8
|
+
import Text from "@sproutsocial/seeds-react-text";
|
|
9
|
+
import type {
|
|
10
|
+
TypeSegmentedControlProps,
|
|
11
|
+
TypeSegmentedControlItemProps,
|
|
12
|
+
} from "./SegmentedControlTypes";
|
|
13
|
+
|
|
14
|
+
export const SegmentedControlContainer = styled(Box)<
|
|
15
|
+
Pick<TypeSegmentedControlProps, "disabled">
|
|
16
|
+
>`
|
|
17
|
+
border: 1px solid
|
|
18
|
+
${(props) => props.theme.colors.button.secondary.border.base};
|
|
19
|
+
border-radius: ${(props) => props.theme.radii.outer};
|
|
20
|
+
padding: ${(props) => props.theme.space[100]};
|
|
21
|
+
|
|
22
|
+
legend {
|
|
23
|
+
${visuallyHidden}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
${(props) => props.disabled && disabled}
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
export const SegmentedControlItemContainer = styled(Box)<
|
|
30
|
+
Pick<TypeSegmentedControlItemProps, "disabled">
|
|
31
|
+
>`
|
|
32
|
+
flex: 1 1 auto;
|
|
33
|
+
display: flex;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
|
|
36
|
+
& + & {
|
|
37
|
+
margin-left: ${(props) => props.theme.space[100]};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&:focus-within label {
|
|
41
|
+
${focusRing}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
input {
|
|
45
|
+
${visuallyHidden}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
${(props) => props.disabled && disabled}
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
interface TypeSegmentedControlState {
|
|
52
|
+
isActive: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const SegmentedControlLabel = styled(Text)<TypeSegmentedControlState>`
|
|
56
|
+
flex: 1 1 auto;
|
|
57
|
+
display: flex;
|
|
58
|
+
align-items: center;
|
|
59
|
+
justify-content: center;
|
|
60
|
+
text-align: center;
|
|
61
|
+
color: ${(props) => props.theme.colors.text.body};
|
|
62
|
+
cursor: pointer;
|
|
63
|
+
font-size: ${(props) => props.theme.typography[200].fontSize};
|
|
64
|
+
/**
|
|
65
|
+
* Matches default line height of Icon. Also matches the overall height of
|
|
66
|
+
* Input, Select, and Button.
|
|
67
|
+
*/
|
|
68
|
+
line-height: 16px;
|
|
69
|
+
font-weight: ${(props) => props.theme.fontWeights.semibold};
|
|
70
|
+
|
|
71
|
+
border-radius: ${(props) => props.theme.radii.inner};
|
|
72
|
+
/* To match the height of buttons... 350 padding - 2px top and bottom padding of the parent - 1px border on top and bottom */
|
|
73
|
+
padding: calc(${(props) => props.theme.space[350]} - 6px);
|
|
74
|
+
transition: all ${(props) => props.theme.duration.fast};
|
|
75
|
+
|
|
76
|
+
&:hover {
|
|
77
|
+
background-color: ${(props) =>
|
|
78
|
+
props.theme.colors.listItem.background.hover};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
${(props) =>
|
|
82
|
+
props.isActive &&
|
|
83
|
+
css`
|
|
84
|
+
color: ${(props) => props.theme.colors.text.inverse};
|
|
85
|
+
background-color: ${(props) =>
|
|
86
|
+
props.theme.colors.listItem.background.selected};
|
|
87
|
+
|
|
88
|
+
&:hover {
|
|
89
|
+
background-color: ${(props) =>
|
|
90
|
+
props.theme.colors.listItem.background.selected};
|
|
91
|
+
}
|
|
92
|
+
`}
|
|
93
|
+
`;
|
package/tsconfig.json
ADDED
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineConfig } from "tsup";
|
|
2
|
+
|
|
3
|
+
export default defineConfig((options) => ({
|
|
4
|
+
entry: ["src/index.ts"],
|
|
5
|
+
format: ["cjs", "esm"],
|
|
6
|
+
clean: true,
|
|
7
|
+
legacyOutput: true,
|
|
8
|
+
dts: options.dts,
|
|
9
|
+
external: ["react"],
|
|
10
|
+
sourcemap: true,
|
|
11
|
+
metafile: options.metafile,
|
|
12
|
+
}));
|