Skip to content

Commit

Permalink
[WIP][base] useNumberInput
Browse files Browse the repository at this point in the history
  • Loading branch information
mj12albert committed Feb 22, 2023
1 parent e3a37cb commit c4a5b72
Show file tree
Hide file tree
Showing 25 changed files with 905 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import * as React from 'react';
import NumberInputUnstyled from '@mui/base/NumberInputUnstyled';
import { styled } from '@mui/system';

const blue = {
100: '#DAECFF',
200: '#b6daff',
400: '#3399FF',
500: '#007FFF',
600: '#0072E5',
};

const grey = {
50: '#f6f8fa',
100: '#eaeef2',
200: '#d0d7de',
300: '#afb8c1',
400: '#8c959f',
500: '#6e7781',
600: '#57606a',
700: '#424a53',
800: '#32383f',
900: '#24292f',
};

const StyledInputElement = styled('input')(
({ theme }) => `
width: 320px;
font-family: IBM Plex Sans, sans-serif;
font-size: 0.875rem;
font-weight: 400;
line-height: 1.5;
padding: 12px;
border-radius: 12px;
color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]};
background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'};
border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]};
box-shadow: 0px 4px 30px ${theme.palette.mode === 'dark' ? grey[900] : grey[200]};
&:hover {
border-color: ${blue[400]};
}
&:focus {
border-color: ${blue[400]};
box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]};
}
// firefox
&:focus-visible {
outline: 0;
}
`,
);

const CustomNumberInput = React.forwardRef(function CustomNumberInput(props, ref) {
return (
<NumberInputUnstyled
slots={{ input: StyledInputElement }}
{...props}
ref={ref}
/>
);
});

export default function UnstyledNumberInputIntroduction() {
return (
<CustomNumberInput aria-label="Demo number input" placeholder="Type a number…" />
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import * as React from 'react';
import NumberInputUnstyled, {
NumberInputUnstyledProps,
} from '@mui/base/NumberInputUnstyled';
import { styled } from '@mui/system';

const blue = {
100: '#DAECFF',
200: '#b6daff',
400: '#3399FF',
500: '#007FFF',
600: '#0072E5',
};

const grey = {
50: '#f6f8fa',
100: '#eaeef2',
200: '#d0d7de',
300: '#afb8c1',
400: '#8c959f',
500: '#6e7781',
600: '#57606a',
700: '#424a53',
800: '#32383f',
900: '#24292f',
};

const StyledInputElement = styled('input')(
({ theme }) => `
width: 320px;
font-family: IBM Plex Sans, sans-serif;
font-size: 0.875rem;
font-weight: 400;
line-height: 1.5;
padding: 12px;
border-radius: 12px;
color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]};
background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'};
border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]};
box-shadow: 0px 4px 30px ${theme.palette.mode === 'dark' ? grey[900] : grey[200]};
&:hover {
border-color: ${blue[400]};
}
&:focus {
border-color: ${blue[400]};
box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]};
}
// firefox
&:focus-visible {
outline: 0;
}
`,
);

const CustomNumberInput = React.forwardRef(function CustomNumberInput(
props: NumberInputUnstyledProps,
ref: React.ForwardedRef<HTMLDivElement>,
) {
return (
<NumberInputUnstyled
slots={{ input: StyledInputElement }}
{...props}
ref={ref}
/>
);
});

export default function UnstyledNumberInputIntroduction() {
return (
<CustomNumberInput aria-label="Demo number input" placeholder="Type a number…" />
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<CustomNumberInput aria-label="Demo number input" placeholder="Type a number…" />
33 changes: 33 additions & 0 deletions docs/data/base/components/number-input/number-input.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
product: base
title: Unstyled React Number Input component and hook
components: NumberInputUnstyled
# hooks: useNumberInput
githubLabel: 'component: NumberInput'
---

# Unstyled Number Input

<p class="description">The Unstyled Number Input component provides users with a field for integer values, and buttons to increment or decrement the value.</p>

## Introduction

🚧

{{"demo": "UnstyledNumberInputIntroduction.js", "defaultCodeOpen": false, "bg": "gradient"}}

{{"component": "modules/components/ComponentLinkHeader.js", "design": false}}

## Component

### Usage

After [installation](/base/getting-started/installation/), you can start building with this component using the following basic elements:

```jsx
import NumberInputUnstyled from '@mui/base/NumberInputUnstyled';

export default function MyApp() {
return <NumberInputUnstyled />;
}
```
1 change: 1 addition & 0 deletions docs/data/base/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const pages = [
children: [
{ pathname: '/base/react-button', title: 'Button' },
{ pathname: '/base/react-input', title: 'Input' },
{ pathname: '/base/react-number-input', title: 'Number Input' },
{ pathname: '/base/react-select', title: 'Select' },
{ pathname: '/base/react-slider', title: 'Slider' },
{ pathname: '/base/react-switch', title: 'Switch' },
Expand Down
2 changes: 2 additions & 0 deletions docs/data/base/pagesApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = [
{ pathname: '/base/api/modal-unstyled' },
{ pathname: '/base/api/multi-select-unstyled' },
{ pathname: '/base/api/no-ssr' },
{ pathname: '/base/api/number-input-unstyled' },
{ pathname: '/base/api/option-group-unstyled' },
{ pathname: '/base/api/option-unstyled' },
{ pathname: '/base/api/popper-unstyled' },
Expand All @@ -31,6 +32,7 @@ module.exports = [
{ pathname: '/base/api/use-input' },
{ pathname: '/base/api/use-menu' },
{ pathname: '/base/api/use-menu-item' },
{ pathname: '/base/api/use-number-input' },
{ pathname: '/base/api/use-option' },
{ pathname: '/base/api/use-select' },
{ pathname: '/base/api/use-slider' },
Expand Down
23 changes: 23 additions & 0 deletions docs/pages/base/api/number-input-unstyled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from 'react';
import ApiPage from 'docs/src/modules/components/ApiPage';
import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations';
import jsonPageContent from './number-input-unstyled.json';

export default function Page(props) {
const { descriptions, pageContent } = props;
return <ApiPage descriptions={descriptions} pageContent={pageContent} />;
}

Page.getInitialProps = () => {
const req = require.context(
'docs/translations/api-docs/number-input-unstyled',
false,
/number-input-unstyled.*.json$/,
);
const descriptions = mapApiPageTranslations(req);

return {
descriptions,
pageContent: jsonPageContent,
};
};
20 changes: 20 additions & 0 deletions docs/pages/base/api/number-input-unstyled.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"props": {
"id": { "type": { "name": "string" } },
"slotProps": {
"type": { "name": "shape", "description": "{ input?: func<br>&#124;&nbsp;object }" },
"default": "{}"
},
"slots": {
"type": { "name": "shape", "description": "{ input?: elementType }" },
"default": "{}"
}
},
"name": "NumberInputUnstyled",
"styles": { "classes": [], "globalClasses": {}, "name": null },
"spread": true,
"filename": "/packages/mui-base/src/NumberInputUnstyled/NumberInputUnstyled.tsx",
"inheritance": null,
"demos": "<ul><li><a href=\"/base/react-number-input/\">hooks: useNumberInput</a></li></ul>",
"cssComponent": false
}
23 changes: 23 additions & 0 deletions docs/pages/base/api/use-number-input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from 'react';
import HookApiPage from 'docs/src/modules/components/HookApiPage';
import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations';
import jsonPageContent from './use-number-input.json';

export default function Page(props) {
const { descriptions, pageContent } = props;
return <HookApiPage descriptions={descriptions} pageContent={pageContent} />;
}

Page.getInitialProps = () => {
const req = require.context(
'docs/translations/api-docs/use-number-input',
false,
/use-number-input.*.json$/,
);
const descriptions = mapApiPageTranslations(req);

return {
descriptions,
pageContent: jsonPageContent,
};
};
37 changes: 37 additions & 0 deletions docs/pages/base/api/use-number-input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"parameters": {
"defaultValue": { "type": { "name": "unknown", "description": "unknown" } },
"disabled": { "type": { "name": "boolean", "description": "boolean" } },
"error": { "type": { "name": "boolean", "description": "boolean" } },
"inputRef": {
"type": {
"name": "React.Ref&lt;HTMLInputElement&gt",
"description": "React.Ref&lt;HTMLInputElement&gt"
}
},
"max": { "type": { "name": "number", "description": "number" } },
"min": { "type": { "name": "number", "description": "number" } },
"onBlur": {
"type": { "name": "React.FocusEventHandler", "description": "React.FocusEventHandler" }
},
"onChange": {
"type": {
"name": "UseNumberInputChangeHandler",
"description": "UseNumberInputChangeHandler"
}
},
"onClick": {
"type": { "name": "React.MouseEventHandler", "description": "React.MouseEventHandler" }
},
"onFocus": {
"type": { "name": "React.FocusEventHandler", "description": "React.FocusEventHandler" }
},
"required": { "type": { "name": "boolean", "description": "boolean" } },
"step": { "type": { "name": "number", "description": "number" } },
"value": { "type": { "name": "unknown", "description": "unknown" } }
},
"returnValue": {},
"name": "useNumberInput",
"filename": "/packages/mui-base/src/NumberInputUnstyled/useNumberInput.ts",
"demos": "<ul></ul>"
}
7 changes: 7 additions & 0 deletions docs/pages/base/react-number-input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as React from 'react';
import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs';
import * as pageProps from 'docs/data/base/components/number-input/number-input.md?@mui/markdown';

export default function Page() {
return <MarkdownDocs {...pageProps} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"componentDescription": "",
"propDescriptions": {
"id": "The id of the <code>input</code> element.",
"slotProps": "The props used for each slot inside the Input.",
"slots": "The components used for each slot inside the InputBase. Either a string to use a HTML element or a component. See <a href=\"#slots\">Slots API</a> below for more details."
},
"classDescriptions": {}
}
10 changes: 10 additions & 0 deletions docs/translations/api-docs/use-number-input/use-number-input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"hookDescription": "",
"parametersDescriptions": {
"defaultValue": "The default value. Use when the component is not controlled.",
"disabled": "If <code>true</code>, the component is disabled.\nThe prop defaults to the value (<code>false</code>) inherited from the parent FormControl component.",
"error": "If <code>true</code>, the <code>input</code> will indicate an error by setting the <code>aria-invalid</code> attribute.\nThe prop defaults to the value (<code>false</code>) inherited from the parent FormControl component.",
"required": "If <code>true</code>, the <code>input</code> element is required.\nThe prop defaults to the value (<code>false</code>) inherited from the parent FormControl component."
},
"returnValueDescriptions": {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,15 @@ const FormControlUnstyled = React.forwardRef(function FormControlUnstyled<
setValue(event.target.value);
onChange?.(event);
},
// TODO: make it like this? to work with SelectUnstyled as well
// onChange: (event: React.ChangeEvent<NativeFormControlElement>, valueTwo?: unknown) => {
// console.group('FormControlUnstyledContext onChange');
// console.log(event);
// console.log('valueTwo', valueTwo);
// console.groupEnd();
// setValue(valueTwo ?? event.target.value);
// onChange?.(event, valueTwo);
// },
onFocus: () => {
setFocused(true);
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as React from 'react';
import { /* createMount, */ createRenderer /* , describeConformanceUnstyled */ } from 'test/utils';
import { expect } from 'chai';
import NumberInputUnstyled from '@mui/base/NumberInputUnstyled';

describe('<InputUnstyled />', () => {
// const mount = createMount();
const { render } = createRenderer();

// TODO: wow this looks complicated
// describeConformanceUnstyled(<NumberInputUnstyled />, () => ({
// inheritComponent: 'div',
// render,
// mount,
// refInstanceof: window.HTMLDivElement,
// testComponentPropWith: 'div',
// muiName: 'MuiInput',
// slots: {
// root: {
// expectedClassName: '',
// },
// input: {
// expectedClassName: '',
// testWithElement: 'input',
// },
// },
// }));

it('should be able to attach input ref passed through props', () => {
const inputRef = React.createRef<HTMLInputElement>();
const { getByRole } = render(<NumberInputUnstyled slotProps={{ input: { ref: inputRef } }} />);

expect(inputRef.current).to.deep.equal(getByRole('spinbutton'));
});
});
Loading

0 comments on commit c4a5b72

Please sign in to comment.