@primer/components 31.2.0-rc.a2a8604e → 31.2.0-rc.c7f73427
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 +4 -0
- package/dist/browser.esm.js +180 -178
- package/dist/browser.esm.js.map +1 -1
- package/dist/browser.umd.js +182 -180
- package/dist/browser.umd.js.map +1 -1
- package/docs/src/@primer/gatsby-theme-doctocat/components/hero.js +1 -3
- package/docs/src/@primer/gatsby-theme-doctocat/components/live-preview-wrapper.js +1 -1
- package/lib/Overlay.js +3 -1
- package/lib/Portal/Portal.js +3 -2
- package/lib/_TextInputWrapper.js +2 -2
- package/lib/__tests__/AnchoredOverlay.test.js +4 -2
- package/lib/__tests__/TextInputWithTokens.test.js +1 -10
- package/lib/hooks/useAnchoredPosition.js +3 -2
- package/lib/hooks/useCombinedRefs.d.ts +2 -2
- package/lib/hooks/useCombinedRefs.js +4 -6
- package/lib/hooks/useResizeObserver.js +2 -2
- package/lib/stories/TextInput.stories.js +144 -0
- package/lib/stories/Token.stories.js +19 -2
- package/lib/utils/useIsomorphicLayoutEffect.d.ts +3 -0
- package/lib/utils/useIsomorphicLayoutEffect.js +12 -0
- package/lib-esm/Overlay.js +2 -1
- package/lib-esm/Portal/Portal.js +2 -1
- package/lib-esm/_TextInputWrapper.js +2 -2
- package/lib-esm/__tests__/AnchoredOverlay.test.js +4 -2
- package/lib-esm/__tests__/TextInputWithTokens.test.js +1 -10
- package/lib-esm/hooks/useAnchoredPosition.js +2 -1
- package/lib-esm/hooks/useCombinedRefs.d.ts +2 -2
- package/lib-esm/hooks/useCombinedRefs.js +3 -2
- package/lib-esm/hooks/useResizeObserver.js +2 -2
- package/lib-esm/stories/TextInput.stories.js +117 -0
- package/lib-esm/stories/Token.stories.js +14 -1
- package/lib-esm/utils/useIsomorphicLayoutEffect.d.ts +3 -0
- package/lib-esm/utils/useIsomorphicLayoutEffect.js +3 -0
- package/migrating.md +1 -1
- package/package.json +2 -2
- package/src/Overlay.tsx +2 -1
- package/src/Portal/Portal.tsx +2 -1
- package/src/_TextInputWrapper.tsx +7 -0
- package/src/__tests__/AnchoredOverlay.test.tsx +2 -2
- package/src/__tests__/TextInputWithTokens.test.tsx +0 -10
- package/src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap +35 -135
- package/src/hooks/useAnchoredPosition.ts +2 -1
- package/src/hooks/useCombinedRefs.ts +3 -3
- package/src/hooks/useResizeObserver.ts +2 -2
- package/src/stories/TextInput.stories.tsx +113 -0
- package/src/stories/Token.stories.tsx +12 -1
- package/src/utils/useIsomorphicLayoutEffect.ts +10 -0
- package/stats.html +1 -1
@@ -0,0 +1,117 @@
|
|
1
|
+
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
2
|
+
|
3
|
+
import React, { useState } from 'react';
|
4
|
+
import { BaseStyles, Box, ThemeProvider, Text } from '..';
|
5
|
+
import TextInput from '../TextInput';
|
6
|
+
import { CheckIcon } from '@primer/octicons-react';
|
7
|
+
export default {
|
8
|
+
title: 'Forms/Text Input',
|
9
|
+
component: TextInput,
|
10
|
+
decorators: [Story => {
|
11
|
+
return /*#__PURE__*/React.createElement(ThemeProvider, null, /*#__PURE__*/React.createElement(BaseStyles, null, /*#__PURE__*/React.createElement(Box, {
|
12
|
+
paddingTop: 5
|
13
|
+
}, Story())));
|
14
|
+
}],
|
15
|
+
argTypes: {
|
16
|
+
sx: {
|
17
|
+
table: {
|
18
|
+
disable: true
|
19
|
+
}
|
20
|
+
},
|
21
|
+
block: {
|
22
|
+
name: 'Block',
|
23
|
+
defaultValue: false,
|
24
|
+
control: {
|
25
|
+
type: 'boolean'
|
26
|
+
}
|
27
|
+
},
|
28
|
+
disabled: {
|
29
|
+
name: 'Disabled',
|
30
|
+
defaultValue: false,
|
31
|
+
control: {
|
32
|
+
type: 'boolean'
|
33
|
+
}
|
34
|
+
},
|
35
|
+
variant: {
|
36
|
+
name: 'Variants',
|
37
|
+
options: ['small', 'medium', 'large'],
|
38
|
+
control: {
|
39
|
+
type: 'radio'
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
};
|
44
|
+
|
45
|
+
const Label = ({
|
46
|
+
htmlFor,
|
47
|
+
children
|
48
|
+
}) => /*#__PURE__*/React.createElement(Text, {
|
49
|
+
as: "label",
|
50
|
+
htmlFor: htmlFor,
|
51
|
+
sx: {
|
52
|
+
fontWeight: 600,
|
53
|
+
fontSize: 14
|
54
|
+
}
|
55
|
+
}, children);
|
56
|
+
|
57
|
+
Label.displayName = "Label";
|
58
|
+
export const Default = args => {
|
59
|
+
const [value, setValue] = useState('');
|
60
|
+
|
61
|
+
const handleChange = event => {
|
62
|
+
setValue(event.target.value);
|
63
|
+
};
|
64
|
+
|
65
|
+
const inputId = 'basic-text-input';
|
66
|
+
return /*#__PURE__*/React.createElement("form", null, /*#__PURE__*/React.createElement("div", {
|
67
|
+
className: "form-group"
|
68
|
+
}, /*#__PURE__*/React.createElement("div", {
|
69
|
+
className: "form-group-header"
|
70
|
+
}, /*#__PURE__*/React.createElement(Label, {
|
71
|
+
htmlFor: inputId
|
72
|
+
}, "Example label")), /*#__PURE__*/React.createElement("div", {
|
73
|
+
className: "form-group-body"
|
74
|
+
}, /*#__PURE__*/React.createElement(TextInput, _extends({
|
75
|
+
id: inputId,
|
76
|
+
value: value,
|
77
|
+
onChange: handleChange
|
78
|
+
}, args)))));
|
79
|
+
};
|
80
|
+
Default.displayName = "Default";
|
81
|
+
export const WithLeadingIcon = args => {
|
82
|
+
const [value, setValue] = useState('');
|
83
|
+
|
84
|
+
const handleChange = event => {
|
85
|
+
setValue(event.target.value);
|
86
|
+
};
|
87
|
+
|
88
|
+
const inputId = 'basic-text-input-with-leading-icon';
|
89
|
+
return /*#__PURE__*/React.createElement("form", null, /*#__PURE__*/React.createElement(Label, {
|
90
|
+
htmlFor: inputId
|
91
|
+
}, "Example label"), /*#__PURE__*/React.createElement("br", null), /*#__PURE__*/React.createElement(TextInput, _extends({
|
92
|
+
icon: CheckIcon,
|
93
|
+
id: inputId,
|
94
|
+
value: value,
|
95
|
+
onChange: handleChange,
|
96
|
+
type: "password"
|
97
|
+
}, args)));
|
98
|
+
};
|
99
|
+
WithLeadingIcon.displayName = "WithLeadingIcon";
|
100
|
+
export const Password = args => {
|
101
|
+
const [value, setValue] = useState('');
|
102
|
+
|
103
|
+
const handleChange = event => {
|
104
|
+
setValue(event.target.value);
|
105
|
+
};
|
106
|
+
|
107
|
+
const inputId = 'basic-text-input-as-password';
|
108
|
+
return /*#__PURE__*/React.createElement("form", null, /*#__PURE__*/React.createElement(Label, {
|
109
|
+
htmlFor: inputId
|
110
|
+
}, "Password"), /*#__PURE__*/React.createElement("br", null), /*#__PURE__*/React.createElement(TextInput, _extends({
|
111
|
+
type: "password",
|
112
|
+
id: inputId,
|
113
|
+
value: value,
|
114
|
+
onChange: handleChange
|
115
|
+
}, args)));
|
116
|
+
};
|
117
|
+
Password.displayName = "Password";
|
@@ -7,6 +7,7 @@ import { BaseStyles, ThemeProvider } from '..';
|
|
7
7
|
import Box from '../Box';
|
8
8
|
import Token from '../Token/Token';
|
9
9
|
import Text from '../Text';
|
10
|
+
import { GitBranchIcon } from '@primer/octicons-react';
|
10
11
|
export default {
|
11
12
|
title: 'Tokens/Default',
|
12
13
|
component: Token,
|
@@ -19,7 +20,7 @@ export default {
|
|
19
20
|
return /*#__PURE__*/React.createElement(ThemeProvider, null, /*#__PURE__*/React.createElement(BaseStyles, null, /*#__PURE__*/React.createElement(Story, null)));
|
20
21
|
}]
|
21
22
|
};
|
22
|
-
const excludedControlKeys = ['id', 'as', 'tabIndex', 'onRemove'];
|
23
|
+
const excludedControlKeys = ['id', 'as', 'tabIndex', 'onRemove', 'leadingVisual'];
|
23
24
|
|
24
25
|
const SingleExampleContainer = ({
|
25
26
|
children,
|
@@ -91,6 +92,18 @@ Interactive.parameters = {
|
|
91
92
|
exclude: [...excludedControlKeys, 'hideRemoveButton', 'text']
|
92
93
|
}
|
93
94
|
};
|
95
|
+
export const WithLeadingVisual = args => {
|
96
|
+
return /*#__PURE__*/React.createElement(ExampleCollectionContainer, null, /*#__PURE__*/React.createElement(Token, _extends({}, args, {
|
97
|
+
leadingVisual: () => /*#__PURE__*/React.createElement(GitBranchIcon, null)
|
98
|
+
})));
|
99
|
+
};
|
100
|
+
WithLeadingVisual.displayName = "WithLeadingVisual";
|
101
|
+
WithLeadingVisual.storyName = 'with leadingVisual';
|
102
|
+
WithLeadingVisual.parameters = {
|
103
|
+
controls: {
|
104
|
+
exclude: [...excludedControlKeys, 'hideRemoveButton']
|
105
|
+
}
|
106
|
+
};
|
94
107
|
export const WithOnRemoveFn = args => {
|
95
108
|
return /*#__PURE__*/React.createElement(ExampleCollectionContainer, null, /*#__PURE__*/React.createElement(SingleExampleContainer, {
|
96
109
|
label: "w/ onRemove passed"
|
@@ -0,0 +1,3 @@
|
|
1
|
+
import { useEffect, useLayoutEffect } from 'react';
|
2
|
+
const useIsomorphicLayoutEffect = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined' ? useLayoutEffect : useEffect;
|
3
|
+
export default useIsomorphicLayoutEffect;
|
package/migrating.md
CHANGED
@@ -110,7 +110,7 @@ There are two ways to change the theme of @primer/components components:
|
|
110
110
|
|
111
111
|
export default () => (
|
112
112
|
<ThemeProvider theme={theme}>
|
113
|
-
<Block color
|
113
|
+
<Block sx={{color: 'bodytext', p: 4}}>
|
114
114
|
<Text fontSize={4}>Hello, world!</Text>
|
115
115
|
</Block>
|
116
116
|
</ThemeProvider>
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@primer/components",
|
3
|
-
"version": "31.2.0-rc.
|
3
|
+
"version": "31.2.0-rc.c7f73427",
|
4
4
|
"description": "Primer react components",
|
5
5
|
"main": "lib/index.js",
|
6
6
|
"module": "lib-esm/index.js",
|
@@ -15,7 +15,7 @@
|
|
15
15
|
"start": "concurrently npm:start:*",
|
16
16
|
"start:docs": "cd docs && npm run develop",
|
17
17
|
"start:storybook": "start-storybook -p 6006",
|
18
|
-
"lint": "eslint '**/*.{js,ts,tsx,md,mdx}'",
|
18
|
+
"lint": "eslint '**/*.{js,ts,tsx,md,mdx}' --max-warnings=0",
|
19
19
|
"lint:fix": "npm run lint -- --fix",
|
20
20
|
"test": "jest",
|
21
21
|
"test:update": "npm run test -- --updateSnapshot",
|
package/src/Overlay.tsx
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
import styled from 'styled-components'
|
2
|
-
import React, {ReactElement, useEffect,
|
2
|
+
import React, {ReactElement, useEffect, useRef} from 'react'
|
3
3
|
import {get, COMMON, SystemPositionProps, SystemCommonProps} from './constants'
|
4
4
|
import {ComponentProps} from './utils/types'
|
5
|
+
import useLayoutEffect from './utils/useIsomorphicLayoutEffect'
|
5
6
|
import {useOverlay, TouchOrMouseEvent} from './hooks'
|
6
7
|
import Portal from './Portal'
|
7
8
|
import sx, {SxProp} from './sx'
|
package/src/Portal/Portal.tsx
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import React from 'react'
|
2
2
|
import {createPortal} from 'react-dom'
|
3
|
+
import useLayoutEffect from '../utils/useIsomorphicLayoutEffect'
|
3
4
|
|
4
5
|
const PRIMER_PORTAL_ROOT_ID = '__primerPortalRoot__'
|
5
6
|
const DEFAULT_PORTAL_CONTAINER_NAME = '__default__'
|
@@ -69,7 +70,7 @@ export const Portal: React.FC<PortalProps> = ({children, onMount, containerName:
|
|
69
70
|
hostElement.style.zIndex = '1'
|
70
71
|
const elementRef = React.useRef(hostElement)
|
71
72
|
|
72
|
-
|
73
|
+
useLayoutEffect(() => {
|
73
74
|
let containerName = _containerName
|
74
75
|
if (containerName === undefined) {
|
75
76
|
containerName = DEFAULT_PORTAL_CONTAINER_NAME
|
@@ -91,6 +91,13 @@ const TextInputWrapper = styled.span<StyledWrapperProps>`
|
|
91
91
|
display: block;
|
92
92
|
width: 100%;
|
93
93
|
`}
|
94
|
+
|
95
|
+
${props =>
|
96
|
+
props.block &&
|
97
|
+
props.hasIcon &&
|
98
|
+
css`
|
99
|
+
display: flex;
|
100
|
+
`}
|
94
101
|
|
95
102
|
// Ensures inputs don't zoom on mobile but are body-font size on desktop
|
96
103
|
@media (min-width: ${get('breakpoints.1')}) {
|
@@ -144,7 +144,7 @@ describe('AnchoredOverlay', () => {
|
|
144
144
|
})
|
145
145
|
|
146
146
|
it('should render consistently when open', () => {
|
147
|
-
const
|
148
|
-
expect(
|
147
|
+
const {container} = HTMLRender(<AnchoredOverlayTestComponent initiallyOpen={true} />)
|
148
|
+
expect(container).toMatchSnapshot()
|
149
149
|
})
|
150
150
|
})
|
@@ -29,16 +29,6 @@ const LabelledTextInputWithTokens: React.FC<TextInputWithTokensProps> = ({onToke
|
|
29
29
|
</>
|
30
30
|
)
|
31
31
|
|
32
|
-
// describe('axe test', () => {
|
33
|
-
// it('should have no axe violations', async () => {
|
34
|
-
// const onRemoveMock = jest.fn()
|
35
|
-
// const {container} = HTMLRender(<LabelledTextInputWithTokens tokens={mockTokens} onTokenRemove={onRemoveMock} />)
|
36
|
-
// const results = await axe(container)
|
37
|
-
// expect(results).toHaveNoViolations()
|
38
|
-
// cleanup()
|
39
|
-
// })
|
40
|
-
// })
|
41
|
-
|
42
32
|
jest.useFakeTimers()
|
43
33
|
|
44
34
|
describe('TextInputWithTokens', () => {
|
@@ -94,9 +94,7 @@ exports[`AnchoredOverlay renders consistently 1`] = `
|
|
94
94
|
`;
|
95
95
|
|
96
96
|
exports[`AnchoredOverlay should render consistently when open 1`] = `
|
97
|
-
|
98
|
-
"asFragment": [Function],
|
99
|
-
"baseElement": .c0 {
|
97
|
+
.c0 {
|
100
98
|
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";
|
101
99
|
line-height: 1.5;
|
102
100
|
color: #24292f;
|
@@ -187,146 +185,48 @@ Object {
|
|
187
185
|
outline: none;
|
188
186
|
}
|
189
187
|
|
190
|
-
<
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
</button>
|
207
|
-
<div
|
208
|
-
id="__primerPortalRoot__"
|
209
|
-
style="position: absolute; top: 0px; left: 0px;"
|
210
|
-
>
|
211
|
-
<div
|
212
|
-
style="position: relative; z-index: 1;"
|
213
|
-
>
|
214
|
-
<div
|
215
|
-
class="c2"
|
216
|
-
data-focus-trap="active"
|
217
|
-
height="auto"
|
218
|
-
role="none"
|
219
|
-
style="top: 4px; left: 0px; --styled-overlay-visibility: visible;"
|
220
|
-
width="auto"
|
221
|
-
>
|
222
|
-
<button
|
223
|
-
class="focus-visible"
|
224
|
-
data-focus-visible-added=""
|
225
|
-
tabindex="0"
|
226
|
-
type="button"
|
227
|
-
>
|
228
|
-
Focusable Child
|
229
|
-
</button>
|
230
|
-
</div>
|
231
|
-
</div>
|
232
|
-
</div>
|
233
|
-
</div>
|
234
|
-
</div>
|
235
|
-
</body>,
|
236
|
-
"container": <div>
|
188
|
+
<div>
|
189
|
+
<div
|
190
|
+
class="c0"
|
191
|
+
color="fg.default"
|
192
|
+
data-portal-root="true"
|
193
|
+
font-family="normal"
|
194
|
+
>
|
195
|
+
<button
|
196
|
+
aria-haspopup="true"
|
197
|
+
aria-labelledby="react-aria-1"
|
198
|
+
class="c1"
|
199
|
+
id="react-aria-1"
|
200
|
+
tabindex="0"
|
201
|
+
>
|
202
|
+
Anchor Button
|
203
|
+
</button>
|
237
204
|
<div
|
238
|
-
|
239
|
-
|
240
|
-
data-portal-root="true"
|
241
|
-
font-family="normal"
|
205
|
+
id="__primerPortalRoot__"
|
206
|
+
style="position: absolute; top: 0px; left: 0px;"
|
242
207
|
>
|
243
|
-
<button
|
244
|
-
aria-haspopup="true"
|
245
|
-
aria-labelledby="react-aria-1"
|
246
|
-
class="ButtonBase-sc-181ps9o-0 Button-xjtz72-0 iRqJHc"
|
247
|
-
id="react-aria-1"
|
248
|
-
tabindex="0"
|
249
|
-
>
|
250
|
-
Anchor Button
|
251
|
-
</button>
|
252
208
|
<div
|
253
|
-
|
254
|
-
style="position: absolute; top: 0px; left: 0px;"
|
209
|
+
style="position: relative; z-index: 1;"
|
255
210
|
>
|
256
211
|
<div
|
257
|
-
|
212
|
+
class="c2"
|
213
|
+
data-focus-trap="active"
|
214
|
+
height="auto"
|
215
|
+
role="none"
|
216
|
+
style="top: 4px; left: 0px; --styled-overlay-visibility: visible;"
|
217
|
+
width="auto"
|
258
218
|
>
|
259
|
-
<
|
260
|
-
class="
|
261
|
-
data-focus-
|
262
|
-
|
263
|
-
|
264
|
-
style="top: 4px; left: 0px; --styled-overlay-visibility: visible;"
|
265
|
-
width="auto"
|
219
|
+
<button
|
220
|
+
class="focus-visible"
|
221
|
+
data-focus-visible-added=""
|
222
|
+
tabindex="0"
|
223
|
+
type="button"
|
266
224
|
>
|
267
|
-
|
268
|
-
|
269
|
-
data-focus-visible-added=""
|
270
|
-
tabindex="0"
|
271
|
-
type="button"
|
272
|
-
>
|
273
|
-
Focusable Child
|
274
|
-
</button>
|
275
|
-
</div>
|
225
|
+
Focusable Child
|
226
|
+
</button>
|
276
227
|
</div>
|
277
228
|
</div>
|
278
229
|
</div>
|
279
|
-
</div
|
280
|
-
|
281
|
-
"findAllByAltText": [Function],
|
282
|
-
"findAllByDisplayValue": [Function],
|
283
|
-
"findAllByLabelText": [Function],
|
284
|
-
"findAllByPlaceholderText": [Function],
|
285
|
-
"findAllByRole": [Function],
|
286
|
-
"findAllByTestId": [Function],
|
287
|
-
"findAllByText": [Function],
|
288
|
-
"findAllByTitle": [Function],
|
289
|
-
"findByAltText": [Function],
|
290
|
-
"findByDisplayValue": [Function],
|
291
|
-
"findByLabelText": [Function],
|
292
|
-
"findByPlaceholderText": [Function],
|
293
|
-
"findByRole": [Function],
|
294
|
-
"findByTestId": [Function],
|
295
|
-
"findByText": [Function],
|
296
|
-
"findByTitle": [Function],
|
297
|
-
"getAllByAltText": [Function],
|
298
|
-
"getAllByDisplayValue": [Function],
|
299
|
-
"getAllByLabelText": [Function],
|
300
|
-
"getAllByPlaceholderText": [Function],
|
301
|
-
"getAllByRole": [Function],
|
302
|
-
"getAllByTestId": [Function],
|
303
|
-
"getAllByText": [Function],
|
304
|
-
"getAllByTitle": [Function],
|
305
|
-
"getByAltText": [Function],
|
306
|
-
"getByDisplayValue": [Function],
|
307
|
-
"getByLabelText": [Function],
|
308
|
-
"getByPlaceholderText": [Function],
|
309
|
-
"getByRole": [Function],
|
310
|
-
"getByTestId": [Function],
|
311
|
-
"getByText": [Function],
|
312
|
-
"getByTitle": [Function],
|
313
|
-
"queryAllByAltText": [Function],
|
314
|
-
"queryAllByDisplayValue": [Function],
|
315
|
-
"queryAllByLabelText": [Function],
|
316
|
-
"queryAllByPlaceholderText": [Function],
|
317
|
-
"queryAllByRole": [Function],
|
318
|
-
"queryAllByTestId": [Function],
|
319
|
-
"queryAllByText": [Function],
|
320
|
-
"queryAllByTitle": [Function],
|
321
|
-
"queryByAltText": [Function],
|
322
|
-
"queryByDisplayValue": [Function],
|
323
|
-
"queryByLabelText": [Function],
|
324
|
-
"queryByPlaceholderText": [Function],
|
325
|
-
"queryByRole": [Function],
|
326
|
-
"queryByTestId": [Function],
|
327
|
-
"queryByText": [Function],
|
328
|
-
"queryByTitle": [Function],
|
329
|
-
"rerender": [Function],
|
330
|
-
"unmount": [Function],
|
331
|
-
}
|
230
|
+
</div>
|
231
|
+
</div>
|
332
232
|
`;
|
@@ -2,6 +2,7 @@ import React from 'react'
|
|
2
2
|
import {PositionSettings, getAnchoredPosition, AnchorPosition} from '../behaviors/anchoredPosition'
|
3
3
|
import {useProvidedRefOrCreate} from './useProvidedRefOrCreate'
|
4
4
|
import {useResizeObserver} from './useResizeObserver'
|
5
|
+
import useLayoutEffect from '../utils/useIsomorphicLayoutEffect'
|
5
6
|
|
6
7
|
export interface AnchoredPositionHookSettings extends Partial<PositionSettings> {
|
7
8
|
floatingElementRef?: React.RefObject<Element>
|
@@ -41,7 +42,7 @@ export function useAnchoredPosition(
|
|
41
42
|
[floatingElementRef, anchorElementRef, ...dependencies]
|
42
43
|
)
|
43
44
|
|
44
|
-
|
45
|
+
useLayoutEffect(updatePosition, [updatePosition])
|
45
46
|
|
46
47
|
useResizeObserver(updatePosition)
|
47
48
|
|
@@ -1,4 +1,5 @@
|
|
1
|
-
import
|
1
|
+
import {ForwardedRef, useRef} from 'react'
|
2
|
+
import useLayoutEffect from '../utils/useIsomorphicLayoutEffect'
|
2
3
|
|
3
4
|
/**
|
4
5
|
* Creates a ref by combining multiple constituent refs. The ref returned by this hook
|
@@ -11,7 +12,7 @@ import React, {ForwardedRef, useRef} from 'react'
|
|
11
12
|
export function useCombinedRefs<T>(...refs: (ForwardedRef<T> | null | undefined)[]) {
|
12
13
|
const combinedRef = useRef<T | null>(null)
|
13
14
|
|
14
|
-
|
15
|
+
useLayoutEffect(() => {
|
15
16
|
function setRefs(current: T | null = null) {
|
16
17
|
for (const ref of refs) {
|
17
18
|
if (!ref) {
|
@@ -32,7 +33,6 @@ export function useCombinedRefs<T>(...refs: (ForwardedRef<T> | null | undefined)
|
|
32
33
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
33
34
|
setRefs(combinedRef.current)
|
34
35
|
}
|
35
|
-
|
36
36
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
37
37
|
}, [...refs, combinedRef.current])
|
38
38
|
|
@@ -1,7 +1,7 @@
|
|
1
|
-
import
|
1
|
+
import useLayoutEffect from '../utils/useIsomorphicLayoutEffect'
|
2
2
|
|
3
3
|
export function useResizeObserver(callback: () => void) {
|
4
|
-
|
4
|
+
useLayoutEffect(() => {
|
5
5
|
const observer = new window.ResizeObserver(() => callback())
|
6
6
|
observer.observe(document.documentElement)
|
7
7
|
return () => {
|
@@ -0,0 +1,113 @@
|
|
1
|
+
import React, {useState, ReactNode} from 'react'
|
2
|
+
import {Meta} from '@storybook/react'
|
3
|
+
|
4
|
+
import {BaseStyles, Box, ThemeProvider, Text} from '..'
|
5
|
+
import TextInput, {TextInputProps} from '../TextInput'
|
6
|
+
import {CheckIcon} from '@primer/octicons-react'
|
7
|
+
|
8
|
+
export default {
|
9
|
+
title: 'Forms/Text Input',
|
10
|
+
component: TextInput,
|
11
|
+
decorators: [
|
12
|
+
Story => {
|
13
|
+
return (
|
14
|
+
<ThemeProvider>
|
15
|
+
<BaseStyles>
|
16
|
+
<Box paddingTop={5}>{Story()}</Box>
|
17
|
+
</BaseStyles>
|
18
|
+
</ThemeProvider>
|
19
|
+
)
|
20
|
+
}
|
21
|
+
],
|
22
|
+
argTypes: {
|
23
|
+
sx: {
|
24
|
+
table: {
|
25
|
+
disable: true
|
26
|
+
}
|
27
|
+
},
|
28
|
+
block: {
|
29
|
+
name: 'Block',
|
30
|
+
defaultValue: false,
|
31
|
+
control: {
|
32
|
+
type: 'boolean'
|
33
|
+
}
|
34
|
+
},
|
35
|
+
disabled: {
|
36
|
+
name: 'Disabled',
|
37
|
+
defaultValue: false,
|
38
|
+
control: {
|
39
|
+
type: 'boolean'
|
40
|
+
}
|
41
|
+
},
|
42
|
+
variant: {
|
43
|
+
name: 'Variants',
|
44
|
+
options: ['small', 'medium', 'large'],
|
45
|
+
control: {type: 'radio'}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
} as Meta
|
49
|
+
|
50
|
+
const Label = ({htmlFor, children}: {htmlFor: string; children: ReactNode}) => (
|
51
|
+
<Text as="label" htmlFor={htmlFor} sx={{fontWeight: 600, fontSize: 14}}>
|
52
|
+
{children}
|
53
|
+
</Text>
|
54
|
+
)
|
55
|
+
|
56
|
+
export const Default = (args: TextInputProps) => {
|
57
|
+
const [value, setValue] = useState('')
|
58
|
+
|
59
|
+
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
60
|
+
setValue(event.target.value)
|
61
|
+
}
|
62
|
+
|
63
|
+
const inputId = 'basic-text-input'
|
64
|
+
|
65
|
+
return (
|
66
|
+
<form>
|
67
|
+
<div className="form-group">
|
68
|
+
<div className="form-group-header">
|
69
|
+
<Label htmlFor={inputId}>Example label</Label>
|
70
|
+
</div>
|
71
|
+
<div className="form-group-body">
|
72
|
+
<TextInput id={inputId} value={value} onChange={handleChange} {...args} />
|
73
|
+
</div>
|
74
|
+
</div>
|
75
|
+
</form>
|
76
|
+
)
|
77
|
+
}
|
78
|
+
|
79
|
+
export const WithLeadingIcon = (args: TextInputProps) => {
|
80
|
+
const [value, setValue] = useState('')
|
81
|
+
|
82
|
+
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
83
|
+
setValue(event.target.value)
|
84
|
+
}
|
85
|
+
|
86
|
+
const inputId = 'basic-text-input-with-leading-icon'
|
87
|
+
|
88
|
+
return (
|
89
|
+
<form>
|
90
|
+
<Label htmlFor={inputId}>Example label</Label>
|
91
|
+
<br />
|
92
|
+
<TextInput icon={CheckIcon} id={inputId} value={value} onChange={handleChange} type="password" {...args} />
|
93
|
+
</form>
|
94
|
+
)
|
95
|
+
}
|
96
|
+
|
97
|
+
export const Password = (args: TextInputProps) => {
|
98
|
+
const [value, setValue] = useState('')
|
99
|
+
|
100
|
+
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
101
|
+
setValue(event.target.value)
|
102
|
+
}
|
103
|
+
|
104
|
+
const inputId = 'basic-text-input-as-password'
|
105
|
+
|
106
|
+
return (
|
107
|
+
<form>
|
108
|
+
<Label htmlFor={inputId}>Password</Label>
|
109
|
+
<br />
|
110
|
+
<TextInput type="password" id={inputId} value={value} onChange={handleChange} {...args} />
|
111
|
+
</form>
|
112
|
+
)
|
113
|
+
}
|
@@ -7,6 +7,7 @@ import {BaseStyles, ThemeProvider} from '..'
|
|
7
7
|
import Box from '../Box'
|
8
8
|
import Token, {TokenProps} from '../Token/Token'
|
9
9
|
import Text from '../Text'
|
10
|
+
import {GitBranchIcon} from '@primer/octicons-react'
|
10
11
|
|
11
12
|
export default {
|
12
13
|
title: 'Tokens/Default',
|
@@ -29,7 +30,7 @@ export default {
|
|
29
30
|
]
|
30
31
|
} as Meta
|
31
32
|
|
32
|
-
const excludedControlKeys = ['id', 'as', 'tabIndex', 'onRemove']
|
33
|
+
const excludedControlKeys = ['id', 'as', 'tabIndex', 'onRemove', 'leadingVisual']
|
33
34
|
|
34
35
|
const SingleExampleContainer: React.FC<{label?: string}> = ({children, label}) => (
|
35
36
|
<Box
|
@@ -94,6 +95,16 @@ export const Interactive = (args: Omit<TokenProps, 'ref' | 'text'>) => {
|
|
94
95
|
}
|
95
96
|
Interactive.parameters = {controls: {exclude: [...excludedControlKeys, 'hideRemoveButton', 'text']}}
|
96
97
|
|
98
|
+
export const WithLeadingVisual = (args: Omit<TokenProps, 'ref'>) => {
|
99
|
+
return (
|
100
|
+
<ExampleCollectionContainer>
|
101
|
+
<Token {...args} leadingVisual={() => <GitBranchIcon />} />
|
102
|
+
</ExampleCollectionContainer>
|
103
|
+
)
|
104
|
+
}
|
105
|
+
WithLeadingVisual.storyName = 'with leadingVisual'
|
106
|
+
WithLeadingVisual.parameters = {controls: {exclude: [...excludedControlKeys, 'hideRemoveButton']}}
|
107
|
+
|
97
108
|
export const WithOnRemoveFn = (args: Omit<TokenProps, 'ref'>) => {
|
98
109
|
return (
|
99
110
|
<ExampleCollectionContainer>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import {useEffect, useLayoutEffect} from 'react'
|
2
|
+
|
3
|
+
const useIsomorphicLayoutEffect =
|
4
|
+
typeof window !== 'undefined' &&
|
5
|
+
typeof window.document !== 'undefined' &&
|
6
|
+
typeof window.document.createElement !== 'undefined'
|
7
|
+
? useLayoutEffect
|
8
|
+
: useEffect
|
9
|
+
|
10
|
+
export default useIsomorphicLayoutEffect
|