@arbor-education/design-system.components 0.3.5 → 0.4.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/CHANGELOG.md +20 -0
- package/dist/components/banner/Banner.d.ts +19 -0
- package/dist/components/banner/Banner.d.ts.map +1 -0
- package/dist/components/banner/Banner.js +33 -0
- package/dist/components/banner/Banner.js.map +1 -0
- package/dist/components/banner/Banner.stories.d.ts +72 -0
- package/dist/components/banner/Banner.stories.d.ts.map +1 -0
- package/dist/components/banner/Banner.stories.js +84 -0
- package/dist/components/banner/Banner.stories.js.map +1 -0
- package/dist/components/banner/Banner.test.d.ts +2 -0
- package/dist/components/banner/Banner.test.d.ts.map +1 -0
- package/dist/components/banner/Banner.test.js +72 -0
- package/dist/components/banner/Banner.test.js.map +1 -0
- package/dist/components/editableText/EditableText.d.ts +10 -0
- package/dist/components/editableText/EditableText.d.ts.map +1 -0
- package/dist/components/editableText/EditableText.js +36 -0
- package/dist/components/editableText/EditableText.js.map +1 -0
- package/dist/components/editableText/EditableText.stories.d.ts +44 -0
- package/dist/components/editableText/EditableText.stories.d.ts.map +1 -0
- package/dist/components/editableText/EditableText.stories.js +94 -0
- package/dist/components/editableText/EditableText.stories.js.map +1 -0
- package/dist/components/editableText/EditableText.test.d.ts +2 -0
- package/dist/components/editableText/EditableText.test.d.ts.map +1 -0
- package/dist/components/editableText/EditableText.test.js +187 -0
- package/dist/components/editableText/EditableText.test.js.map +1 -0
- package/dist/components/heading/Heading.stories.d.ts +26 -0
- package/dist/components/heading/Heading.stories.d.ts.map +1 -1
- package/dist/components/heading/Heading.stories.js +35 -0
- package/dist/components/heading/Heading.stories.js.map +1 -1
- package/dist/components/progress/Progress.d.ts +6 -0
- package/dist/components/progress/Progress.d.ts.map +1 -0
- package/dist/components/progress/Progress.js +9 -0
- package/dist/components/progress/Progress.js.map +1 -0
- package/dist/components/progress/Progress.stories.d.ts +324 -0
- package/dist/components/progress/Progress.stories.d.ts.map +1 -0
- package/dist/components/progress/Progress.stories.js +77 -0
- package/dist/components/progress/Progress.stories.js.map +1 -0
- package/dist/components/progress/Progress.test.d.ts +2 -0
- package/dist/components/progress/Progress.test.d.ts.map +1 -0
- package/dist/components/progress/Progress.test.js +77 -0
- package/dist/components/progress/Progress.test.js.map +1 -0
- package/dist/components/toast/Toast.d.ts +10 -0
- package/dist/components/toast/Toast.d.ts.map +1 -0
- package/dist/components/toast/Toast.js +20 -0
- package/dist/components/toast/Toast.js.map +1 -0
- package/dist/components/toast/Toast.stories.d.ts +12 -0
- package/dist/components/toast/Toast.stories.d.ts.map +1 -0
- package/dist/components/toast/Toast.stories.js +73 -0
- package/dist/components/toast/Toast.stories.js.map +1 -0
- package/dist/components/toast/Toast.test.d.ts +2 -0
- package/dist/components/toast/Toast.test.d.ts.map +1 -0
- package/dist/components/toast/Toast.test.js +87 -0
- package/dist/components/toast/Toast.test.js.map +1 -0
- package/dist/components/toast/ToastViewport.d.ts +3 -0
- package/dist/components/toast/ToastViewport.d.ts.map +1 -0
- package/dist/components/toast/ToastViewport.js +5 -0
- package/dist/components/toast/ToastViewport.js.map +1 -0
- package/dist/index.css +202 -56
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/setupTestRuntime.ts +7 -0
- package/src/components/banner/Banner.stories.tsx +96 -0
- package/src/components/banner/Banner.test.tsx +86 -0
- package/src/components/banner/Banner.tsx +81 -0
- package/src/components/banner/banner.scss +67 -0
- package/src/components/button/button.scss +1 -5
- package/src/components/card/card.scss +0 -3
- package/src/components/dropdown/dropdown.scss +0 -3
- package/src/components/editableText/EditableText.stories.tsx +136 -0
- package/src/components/editableText/EditableText.test.tsx +242 -0
- package/src/components/editableText/EditableText.tsx +73 -0
- package/src/components/editableText/editableText.scss +54 -0
- package/src/components/formField/fieldset/fieldset.scss +0 -2
- package/src/components/formField/formField.scss +0 -2
- package/src/components/formField/inputs/checkbox/checkboxInput.scss +0 -2
- package/src/components/formField/inputs/input.scss +0 -3
- package/src/components/formField/inputs/radio/radioButtonInput.scss +0 -2
- package/src/components/formField/inputs/selectDropdown/selectDropdown.scss +0 -1
- package/src/components/formField/label/label.scss +0 -2
- package/src/components/heading/Heading.stories.tsx +58 -0
- package/src/components/heading/heading.scss +4 -4
- package/src/components/modal/modal.scss +0 -3
- package/src/components/pill/pill.scss +0 -3
- package/src/components/progress/Progress.stories.tsx +90 -0
- package/src/components/progress/Progress.test.tsx +88 -0
- package/src/components/progress/Progress.tsx +16 -0
- package/src/components/progress/progress.scss +13 -0
- package/src/components/searchBar/searchBar.scss +0 -3
- package/src/components/table/columnFilters/columnFilters.scss +0 -6
- package/src/components/table/pagination/pagination.scss +0 -4
- package/src/components/tabs/tabs.scss +0 -2
- package/src/components/tag/tag.scss +0 -3
- package/src/components/toast/Toast.stories.tsx +113 -0
- package/src/components/toast/Toast.test.tsx +126 -0
- package/src/components/toast/Toast.tsx +35 -0
- package/src/components/toast/ToastViewport.tsx +6 -0
- package/src/components/toast/toast.scss +79 -0
- package/src/components/tooltip/tooltip.scss +0 -3
- package/src/global.scss +9 -1
- package/src/index.scss +4 -0
- package/src/index.ts +4 -0
- package/src/tokens.scss +2 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
4
|
+
import '@testing-library/jest-dom/vitest';
|
|
5
|
+
import { EditableText } from './EditableText';
|
|
6
|
+
|
|
7
|
+
describe('EditableText', () => {
|
|
8
|
+
const defaultProps = {
|
|
9
|
+
text: 'Test Text',
|
|
10
|
+
onEditSave: vi.fn(),
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
it('renders as a span element', () => {
|
|
14
|
+
const { container } = render(<EditableText {...defaultProps} />);
|
|
15
|
+
const span = container.querySelector('.ds-editable-text');
|
|
16
|
+
expect(span).toBeInTheDocument();
|
|
17
|
+
expect(span?.tagName).toBe('SPAN');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('displays the text in view mode', () => {
|
|
21
|
+
render(<EditableText {...defaultProps} />);
|
|
22
|
+
const button = screen.getByRole('button', { name: /edit text/i });
|
|
23
|
+
expect(button).toHaveTextContent('Test Text');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('applies custom className', () => {
|
|
27
|
+
const { container } = render(<EditableText {...defaultProps} className="custom-class" />);
|
|
28
|
+
const span = container.querySelector('.ds-editable-text');
|
|
29
|
+
expect(span).toHaveClass('ds-editable-text', 'custom-class');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('renders edit button with pencil icon in view mode', () => {
|
|
33
|
+
render(<EditableText {...defaultProps} />);
|
|
34
|
+
const button = screen.getByRole('button', { name: /edit text/i });
|
|
35
|
+
expect(button).toBeInTheDocument();
|
|
36
|
+
expect(button.querySelector('.ds-editable-text__button-icon')).toBeInTheDocument();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('enters edit mode when edit button is clicked', async () => {
|
|
40
|
+
const user = userEvent.setup();
|
|
41
|
+
render(<EditableText {...defaultProps} />);
|
|
42
|
+
|
|
43
|
+
const button = screen.getByRole('button', { name: /edit text/i });
|
|
44
|
+
await user.click(button);
|
|
45
|
+
|
|
46
|
+
const input = screen.getByRole('textbox', { name: /text/i });
|
|
47
|
+
expect(input).toBeInTheDocument();
|
|
48
|
+
expect(input).toHaveValue('Test Text');
|
|
49
|
+
expect(input).toHaveFocus();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('calls onEditSave when Enter key is pressed', async () => {
|
|
53
|
+
const user = userEvent.setup();
|
|
54
|
+
const onEditSave = vi.fn();
|
|
55
|
+
render(<EditableText {...defaultProps} onEditSave={onEditSave} />);
|
|
56
|
+
|
|
57
|
+
const button = screen.getByRole('button', { name: /edit text/i });
|
|
58
|
+
await user.click(button);
|
|
59
|
+
|
|
60
|
+
const input = screen.getByRole('textbox', { name: /text/i });
|
|
61
|
+
await user.clear(input);
|
|
62
|
+
await user.type(input, 'New Text');
|
|
63
|
+
await user.keyboard('{Enter}');
|
|
64
|
+
|
|
65
|
+
expect(onEditSave).toHaveBeenCalledWith('New Text');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('calls onEditSave when input is blurred', async () => {
|
|
69
|
+
const user = userEvent.setup();
|
|
70
|
+
const onEditSave = vi.fn();
|
|
71
|
+
render(<EditableText {...defaultProps} onEditSave={onEditSave} />);
|
|
72
|
+
|
|
73
|
+
const button = screen.getByRole('button', { name: /edit text/i });
|
|
74
|
+
await user.click(button);
|
|
75
|
+
|
|
76
|
+
const input = screen.getByRole('textbox', { name: /text/i });
|
|
77
|
+
await user.clear(input);
|
|
78
|
+
await user.type(input, 'Updated Text');
|
|
79
|
+
await user.tab(); // Blur the input
|
|
80
|
+
|
|
81
|
+
await waitFor(() => {
|
|
82
|
+
expect(onEditSave).toHaveBeenCalledWith('Updated Text');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('exits edit mode after saving', async () => {
|
|
87
|
+
const user = userEvent.setup();
|
|
88
|
+
render(<EditableText {...defaultProps} />);
|
|
89
|
+
|
|
90
|
+
const button = screen.getByRole('button', { name: /edit text/i });
|
|
91
|
+
await user.click(button);
|
|
92
|
+
|
|
93
|
+
const input = screen.getByRole('textbox', { name: /text/i });
|
|
94
|
+
await user.keyboard('{Enter}');
|
|
95
|
+
|
|
96
|
+
await waitFor(() => {
|
|
97
|
+
expect(input).not.toBeInTheDocument();
|
|
98
|
+
expect(screen.getByRole('button', { name: /edit text/i })).toBeInTheDocument();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('reverts text when Escape key is pressed', async () => {
|
|
103
|
+
const user = userEvent.setup();
|
|
104
|
+
const onEditSave = vi.fn();
|
|
105
|
+
render(<EditableText {...defaultProps} onEditSave={onEditSave} />);
|
|
106
|
+
|
|
107
|
+
const button = screen.getByRole('button', { name: /edit text/i });
|
|
108
|
+
await user.click(button);
|
|
109
|
+
|
|
110
|
+
const input = screen.getByRole('textbox', { name: /text/i });
|
|
111
|
+
await user.clear(input);
|
|
112
|
+
await user.type(input, 'Changed Text');
|
|
113
|
+
await user.keyboard('{Escape}');
|
|
114
|
+
|
|
115
|
+
await waitFor(() => {
|
|
116
|
+
expect(onEditSave).not.toHaveBeenCalled();
|
|
117
|
+
const viewButton = screen.getByRole('button', { name: /edit text/i });
|
|
118
|
+
expect(viewButton).toHaveTextContent('Test Text');
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('updates text when text prop changes', () => {
|
|
123
|
+
const { rerender } = render(<EditableText {...defaultProps} text="Original" />);
|
|
124
|
+
|
|
125
|
+
expect(screen.getByRole('button', { name: /edit text/i })).toHaveTextContent('Original');
|
|
126
|
+
|
|
127
|
+
rerender(<EditableText {...defaultProps} text="Updated" />);
|
|
128
|
+
|
|
129
|
+
expect(screen.getByRole('button', { name: /edit text/i })).toHaveTextContent('Updated');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('starts in edit mode when isEditing prop is true', () => {
|
|
133
|
+
render(<EditableText {...defaultProps} isEditing={true} />);
|
|
134
|
+
|
|
135
|
+
const input = screen.getByRole('textbox', { name: /text/i });
|
|
136
|
+
expect(input).toBeInTheDocument();
|
|
137
|
+
expect(input).toHaveValue('Test Text');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('switches to edit mode when isEditing prop changes to true', () => {
|
|
141
|
+
const { rerender } = render(<EditableText {...defaultProps} isEditing={false} />);
|
|
142
|
+
|
|
143
|
+
expect(screen.getByRole('button', { name: /edit text/i })).toBeInTheDocument();
|
|
144
|
+
|
|
145
|
+
rerender(<EditableText {...defaultProps} isEditing={true} />);
|
|
146
|
+
|
|
147
|
+
expect(screen.getByRole('textbox', { name: /text/i })).toBeInTheDocument();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('preserves text value across edit sessions', async () => {
|
|
151
|
+
const user = userEvent.setup();
|
|
152
|
+
const onEditSave = vi.fn();
|
|
153
|
+
render(<EditableText {...defaultProps} onEditSave={onEditSave} />);
|
|
154
|
+
|
|
155
|
+
// First edit
|
|
156
|
+
const button = screen.getByRole('button', { name: /edit text/i });
|
|
157
|
+
await user.click(button);
|
|
158
|
+
const input = screen.getByRole('textbox', { name: /text/i });
|
|
159
|
+
await user.clear(input);
|
|
160
|
+
await user.type(input, 'First Edit');
|
|
161
|
+
await user.keyboard('{Enter}');
|
|
162
|
+
|
|
163
|
+
await waitFor(() => {
|
|
164
|
+
expect(onEditSave).toHaveBeenCalledWith('First Edit');
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Second edit - should start with previous saved value
|
|
168
|
+
const buttonAgain = await screen.findByRole('button', { name: /edit text/i });
|
|
169
|
+
await user.click(buttonAgain);
|
|
170
|
+
const inputAgain = screen.getByRole('textbox', { name: /text/i });
|
|
171
|
+
expect(inputAgain).toHaveValue('First Edit');
|
|
172
|
+
|
|
173
|
+
await user.clear(inputAgain);
|
|
174
|
+
await user.type(inputAgain, 'Second Edit');
|
|
175
|
+
await user.keyboard('{Escape}'); // Cancel this time
|
|
176
|
+
|
|
177
|
+
// Should still have first edit value
|
|
178
|
+
await waitFor(() => {
|
|
179
|
+
const finalButton = screen.getByRole('button', { name: /edit text/i });
|
|
180
|
+
expect(finalButton).toHaveTextContent('First Edit');
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('inherits styles from parent element', () => {
|
|
185
|
+
const { container } = render(
|
|
186
|
+
<h1 style={{ fontSize: '32px', fontWeight: 'bold' }}>
|
|
187
|
+
<EditableText {...defaultProps} />
|
|
188
|
+
</h1>,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const span = container.querySelector('.ds-editable-text');
|
|
192
|
+
expect(span).toBeInTheDocument();
|
|
193
|
+
// Component should inherit font-size and font-weight from parent h1
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('renders textarea when multiline is true', async () => {
|
|
197
|
+
const user = userEvent.setup();
|
|
198
|
+
render(<EditableText {...defaultProps} multiline={true} />);
|
|
199
|
+
|
|
200
|
+
const button = screen.getByRole('button', { name: /edit text/i });
|
|
201
|
+
await user.click(button);
|
|
202
|
+
|
|
203
|
+
const textarea = screen.getByRole('textbox', { name: /text/i });
|
|
204
|
+
expect(textarea).toBeInTheDocument();
|
|
205
|
+
expect(textarea.tagName).toBe('TEXTAREA');
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('does not save on Enter key when multiline is true', async () => {
|
|
209
|
+
const user = userEvent.setup();
|
|
210
|
+
const onEditSave = vi.fn();
|
|
211
|
+
render(<EditableText {...defaultProps} onEditSave={onEditSave} multiline={true} />);
|
|
212
|
+
|
|
213
|
+
const button = screen.getByRole('button', { name: /edit text/i });
|
|
214
|
+
await user.click(button);
|
|
215
|
+
|
|
216
|
+
const textarea = screen.getByRole('textbox', { name: /text/i });
|
|
217
|
+
await user.clear(textarea);
|
|
218
|
+
await user.type(textarea, 'Line 1{Enter}Line 2');
|
|
219
|
+
|
|
220
|
+
// Enter should NOT trigger save in multiline mode
|
|
221
|
+
expect(onEditSave).not.toHaveBeenCalled();
|
|
222
|
+
expect(textarea).toHaveValue('Line 1\nLine 2');
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('saves multiline text on blur', async () => {
|
|
226
|
+
const user = userEvent.setup();
|
|
227
|
+
const onEditSave = vi.fn();
|
|
228
|
+
render(<EditableText {...defaultProps} onEditSave={onEditSave} multiline={true} />);
|
|
229
|
+
|
|
230
|
+
const button = screen.getByRole('button', { name: /edit text/i });
|
|
231
|
+
await user.click(button);
|
|
232
|
+
|
|
233
|
+
const textarea = screen.getByRole('textbox', { name: /text/i });
|
|
234
|
+
await user.clear(textarea);
|
|
235
|
+
await user.type(textarea, 'Line 1{Enter}Line 2{Enter}Line 3');
|
|
236
|
+
await user.tab(); // Blur the textarea
|
|
237
|
+
|
|
238
|
+
await waitFor(() => {
|
|
239
|
+
expect(onEditSave).toHaveBeenCalledWith('Line 1\nLine 2\nLine 3');
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import { TextInput } from 'Components/formField/inputs/text/TextInput';
|
|
3
|
+
import { TextArea } from 'Components/formField/inputs/textArea/TextArea';
|
|
4
|
+
import { Icon } from 'Components/icon/Icon';
|
|
5
|
+
import { useEffect, useRef, useState, type KeyboardEvent } from 'react';
|
|
6
|
+
|
|
7
|
+
type EditableTextProps = {
|
|
8
|
+
text: string;
|
|
9
|
+
className?: string;
|
|
10
|
+
onEditSave: (newText: string) => void;
|
|
11
|
+
isEditing?: boolean;
|
|
12
|
+
multiline?: boolean;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const EditableText = ({ text: textProp, className = '', onEditSave, isEditing: isEditingProp = false, multiline = false }: EditableTextProps) => {
|
|
16
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
17
|
+
const [text, setText] = useState(textProp);
|
|
18
|
+
const preEditText = useRef(textProp);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
setText(textProp);
|
|
22
|
+
}, [textProp]);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
setIsEditing(isEditingProp);
|
|
26
|
+
}, [isEditingProp]);
|
|
27
|
+
|
|
28
|
+
const handleEditSave = () => {
|
|
29
|
+
onEditSave(text);
|
|
30
|
+
preEditText.current = text;
|
|
31
|
+
setIsEditing(false);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
|
35
|
+
if (e.key === 'Enter' && !multiline) {
|
|
36
|
+
handleEditSave();
|
|
37
|
+
}
|
|
38
|
+
if (e.key === 'Escape') {
|
|
39
|
+
setText(preEditText.current);
|
|
40
|
+
setIsEditing(false);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const InputComponent = multiline ? TextArea : TextInput;
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<span className={classNames('ds-editable-text', className)}>
|
|
48
|
+
{isEditing
|
|
49
|
+
? (
|
|
50
|
+
<InputComponent
|
|
51
|
+
name="editable-text"
|
|
52
|
+
aria-label="Text"
|
|
53
|
+
className="ds-editable-text__input"
|
|
54
|
+
value={text}
|
|
55
|
+
onChange={e => setText(e.target.value)}
|
|
56
|
+
onBlur={handleEditSave}
|
|
57
|
+
onKeyDown={handleKeyDown}
|
|
58
|
+
autoFocus
|
|
59
|
+
/>
|
|
60
|
+
)
|
|
61
|
+
: (
|
|
62
|
+
<button
|
|
63
|
+
className="ds-editable-text__button"
|
|
64
|
+
aria-label="Edit text"
|
|
65
|
+
onClick={() => setIsEditing(true)}
|
|
66
|
+
>
|
|
67
|
+
{text}
|
|
68
|
+
<Icon name="pencil" size={16} className="ds-editable-text__button-icon" />
|
|
69
|
+
</button>
|
|
70
|
+
)}
|
|
71
|
+
</span>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
.ds-editable-text {
|
|
2
|
+
width: 100%;
|
|
3
|
+
box-sizing: border-box;
|
|
4
|
+
font-size: inherit;
|
|
5
|
+
font-weight: inherit;
|
|
6
|
+
font-family: inherit;
|
|
7
|
+
line-height: inherit;
|
|
8
|
+
color: inherit;
|
|
9
|
+
text-align: inherit;
|
|
10
|
+
text-decoration: inherit;
|
|
11
|
+
text-transform: inherit;
|
|
12
|
+
letter-spacing: inherit;
|
|
13
|
+
|
|
14
|
+
&__input, &__button {
|
|
15
|
+
width: 100%;
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
height: auto;
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: flex-start;
|
|
21
|
+
min-width: 0;
|
|
22
|
+
font-size: inherit;
|
|
23
|
+
font-weight: inherit;
|
|
24
|
+
font-family: inherit;
|
|
25
|
+
line-height: inherit;
|
|
26
|
+
color: inherit;
|
|
27
|
+
text-align: inherit;
|
|
28
|
+
text-decoration: inherit;
|
|
29
|
+
text-transform: inherit;
|
|
30
|
+
letter-spacing: inherit;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.ds-input--M {
|
|
34
|
+
height: auto;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
&__button {
|
|
38
|
+
border: var(--border-weight) solid transparent;
|
|
39
|
+
border-radius: var(--form-field-radius);
|
|
40
|
+
background-color: transparent;
|
|
41
|
+
padding: 0 var(--form-field-spacing-horizontal);
|
|
42
|
+
cursor: pointer;
|
|
43
|
+
|
|
44
|
+
&:hover {
|
|
45
|
+
background-color: var(--color-grey-100);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&__button-icon {
|
|
50
|
+
color: var(--color-grey-500);
|
|
51
|
+
margin-left: var(--spacing-small);
|
|
52
|
+
flex-shrink: 0;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
.ds-input {
|
|
4
4
|
width: 100%;
|
|
5
|
-
font-size: var(--type-body-p-size);
|
|
6
|
-
font-family: var(--font-family-standard);
|
|
7
|
-
font-weight: var(--font-weight-regular);
|
|
8
5
|
border: var(--border-weight) solid var(--form-field-text-default-color-border);
|
|
9
6
|
border-radius: var(--form-field-radius);
|
|
10
7
|
color: var(--form-field-text-default-color-text);
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
.ds-select-dropdown__items--header {
|
|
17
17
|
margin: 0;
|
|
18
18
|
color: var(--form-dropdown-form-drop-item-title-color-text);
|
|
19
|
-
font-size: var(--font-size-2-13);
|
|
20
19
|
padding: var(--spacing-xsmall) var(--spacing-small) var(--spacing-medium) var(--spacing-small);
|
|
21
20
|
border-bottom: var(--border-weight) solid var(--page-base-color-border);
|
|
22
21
|
}
|
|
@@ -2,6 +2,7 @@ import type { Meta } from '@storybook/react-vite';
|
|
|
2
2
|
import { Heading } from './Heading';
|
|
3
3
|
import { Button } from '../button/Button';
|
|
4
4
|
import { Icon } from '../icon/Icon';
|
|
5
|
+
import { EditableText } from 'Components/editableText/EditableText';
|
|
5
6
|
|
|
6
7
|
const meta: Meta<typeof Heading> = {
|
|
7
8
|
title: 'Components/Heading',
|
|
@@ -13,6 +14,14 @@ export const Default = {
|
|
|
13
14
|
children: ['Heading Text'],
|
|
14
15
|
level: 1,
|
|
15
16
|
},
|
|
17
|
+
argTypes: {
|
|
18
|
+
level: {
|
|
19
|
+
control: 'select',
|
|
20
|
+
options: [1, 2, 3, 4],
|
|
21
|
+
description: 'Heading level (h1-h4)',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
tags: ['autodocs'],
|
|
16
25
|
};
|
|
17
26
|
|
|
18
27
|
export const FloatingChildren = {
|
|
@@ -81,4 +90,53 @@ export const HeadingWithButtonsBothSides = {
|
|
|
81
90
|
},
|
|
82
91
|
};
|
|
83
92
|
|
|
93
|
+
export const HeadingWithEditableText = {
|
|
94
|
+
args: {
|
|
95
|
+
level: 1,
|
|
96
|
+
children: [
|
|
97
|
+
<EditableText key={2} text="Editable Heading" onEditSave={() => {}} />,
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export const HeadingWithEditableTextAndButton = {
|
|
103
|
+
args: {
|
|
104
|
+
level: 1,
|
|
105
|
+
children: [
|
|
106
|
+
<Heading.InnerContainer key={1}><EditableText text="Editable Heading" onEditSave={() => {}} /></Heading.InnerContainer>,
|
|
107
|
+
<Heading.InnerContainer key={2}>
|
|
108
|
+
<Button>
|
|
109
|
+
<Icon name="info" />
|
|
110
|
+
Button Text
|
|
111
|
+
</Button>
|
|
112
|
+
</Heading.InnerContainer>,
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export const HeadingWithEditableTextAndButtonsBothSides = {
|
|
118
|
+
args: {
|
|
119
|
+
level: 1,
|
|
120
|
+
children: [
|
|
121
|
+
<Heading.InnerContainer key={1} className="medium-spacing-gap">
|
|
122
|
+
<Button variant="tertiary">
|
|
123
|
+
<Icon name="chevron-left" />
|
|
124
|
+
Button Text
|
|
125
|
+
</Button>
|
|
126
|
+
<EditableText text="Editable Heading" onEditSave={() => {}} />
|
|
127
|
+
</Heading.InnerContainer>,
|
|
128
|
+
<Heading.InnerContainer key={2} className="medium-spacing-gap">
|
|
129
|
+
<Button variant="secondary">
|
|
130
|
+
<Icon name="info" />
|
|
131
|
+
Button Text
|
|
132
|
+
</Button>
|
|
133
|
+
<Button>
|
|
134
|
+
<Icon name="info" />
|
|
135
|
+
Button Text
|
|
136
|
+
</Button>
|
|
137
|
+
</Heading.InnerContainer>,
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
84
142
|
export default meta;
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
h1 {
|
|
20
|
-
&.ds-heading {
|
|
20
|
+
&.ds-heading, .ds-editable-text__button, .ds-editable-text__input {
|
|
21
21
|
font-family: var(--type-headings-h1-family);
|
|
22
22
|
font-size: var(--type-headings-h1-size);
|
|
23
23
|
font-weight: var(--type-headings-h1-weight);
|
|
@@ -26,7 +26,7 @@ h1 {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
h2 {
|
|
29
|
-
&.ds-heading {
|
|
29
|
+
&.ds-heading, .ds-editable-text__button, .ds-editable-text__input {
|
|
30
30
|
font-family: var(--type-headings-h2-family);
|
|
31
31
|
font-size: var(--type-headings-h2-size);
|
|
32
32
|
font-weight: var(--type-headings-h2-weight);
|
|
@@ -35,7 +35,7 @@ h2 {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
h3 {
|
|
38
|
-
&.ds-heading {
|
|
38
|
+
&.ds-heading, .ds-editable-text__button, .ds-editable-text__input {
|
|
39
39
|
font-family: var(--type-headings-h3-family);
|
|
40
40
|
font-size: var(--type-headings-h3-size);
|
|
41
41
|
font-weight: var(--type-headings-h3-weight);
|
|
@@ -44,7 +44,7 @@ h3 {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
h4 {
|
|
47
|
-
&.ds-heading {
|
|
47
|
+
&.ds-heading, .ds-editable-text__button, .ds-editable-text__input {
|
|
48
48
|
font-family: var(--type-headings-h4-family);
|
|
49
49
|
font-size: var(--type-headings-h4-size);
|
|
50
50
|
font-weight: var(--type-headings-h4-weight);
|
|
@@ -23,9 +23,6 @@
|
|
|
23
23
|
max-width: calc(100vw - var(--spacing-medium));
|
|
24
24
|
max-height: calc(100vh - var(--spacing-medium));
|
|
25
25
|
min-width: var(--modal-min-width);
|
|
26
|
-
font-family: var(--type-body-p-family);
|
|
27
|
-
font-size: var(--type-body-p-size);
|
|
28
|
-
font-weight: var(--type-body-p-weight);
|
|
29
26
|
line-height: var(--type-body-line-height);
|
|
30
27
|
color: var(--type-body-p-color);
|
|
31
28
|
|
|
@@ -10,10 +10,7 @@
|
|
|
10
10
|
cursor: pointer;
|
|
11
11
|
|
|
12
12
|
/* typography/body/p1-reg */
|
|
13
|
-
font-family: var(--type-body-p-family, Inter);
|
|
14
|
-
font-size: var(--type-body-p-size);
|
|
15
13
|
font-style: normal;
|
|
16
|
-
font-weight: var(--type-body-p-weight);
|
|
17
14
|
line-height: 150%; /* 19.5px */
|
|
18
15
|
|
|
19
16
|
&__inactive{
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { Progress } from './Progress';
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Components/Progress',
|
|
6
|
+
component: Progress,
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: 'centered',
|
|
9
|
+
},
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
args: {
|
|
12
|
+
'aria-label': 'Progress bar',
|
|
13
|
+
},
|
|
14
|
+
argTypes: {
|
|
15
|
+
value: {
|
|
16
|
+
control: { type: 'number', min: 0, max: 100 },
|
|
17
|
+
description: 'Current progress value',
|
|
18
|
+
},
|
|
19
|
+
max: {
|
|
20
|
+
control: 'number',
|
|
21
|
+
description: 'Maximum progress value',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
decorators: [
|
|
25
|
+
Story => (
|
|
26
|
+
<div style={{ width: '400px' }}>
|
|
27
|
+
<Story />
|
|
28
|
+
</div>
|
|
29
|
+
),
|
|
30
|
+
],
|
|
31
|
+
} satisfies Meta<typeof Progress>;
|
|
32
|
+
|
|
33
|
+
export default meta;
|
|
34
|
+
type Story = StoryObj<typeof meta>;
|
|
35
|
+
|
|
36
|
+
// Default progress
|
|
37
|
+
export const Default: Story = {
|
|
38
|
+
args: {
|
|
39
|
+
value: 50,
|
|
40
|
+
max: 100,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Empty progress
|
|
45
|
+
export const Empty: Story = {
|
|
46
|
+
args: {
|
|
47
|
+
value: 0,
|
|
48
|
+
max: 100,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Quarter progress
|
|
53
|
+
export const Quarter: Story = {
|
|
54
|
+
args: {
|
|
55
|
+
value: 25,
|
|
56
|
+
max: 100,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Half progress
|
|
61
|
+
export const Half: Story = {
|
|
62
|
+
args: {
|
|
63
|
+
value: 50,
|
|
64
|
+
max: 100,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Three quarters progress
|
|
69
|
+
export const ThreeQuarters: Story = {
|
|
70
|
+
args: {
|
|
71
|
+
value: 75,
|
|
72
|
+
max: 100,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Complete progress
|
|
77
|
+
export const Complete: Story = {
|
|
78
|
+
args: {
|
|
79
|
+
value: 100,
|
|
80
|
+
max: 100,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Custom max value
|
|
85
|
+
export const CustomMax: Story = {
|
|
86
|
+
args: {
|
|
87
|
+
value: 30,
|
|
88
|
+
max: 50,
|
|
89
|
+
},
|
|
90
|
+
};
|