Skip to content

Commit

Permalink
feat(modal): add new initiallyOpen prop (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
Meemaw authored Apr 7, 2019
1 parent 5ce2936 commit 98e64a8
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 26 deletions.
1 change: 1 addition & 0 deletions .storybook/addons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '@storybook/addon-a11y/register';
5 changes: 4 additions & 1 deletion .storybook/config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { configure } from '@storybook/react';
import { addDecorator, configure } from '@storybook/react';
import { withA11y } from '@storybook/addon-a11y';

addDecorator(withA11y);

configure(() => require('../stories/index'), module);
4 changes: 4 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ import MicroModal from 'react-micro-modal';
Function that recieves a handleOpen function and should render a modal trigger. (REQUIRED if used as a uncontrolled component)
*/
trigger={handleOpen => <div onClick={handleOpen}>Open!</div>}
/*
Boolean describing if the modal should be open on first render. (if used as a uncontrolled component, else just use open)
*/
initiallyOpen={false}
/*
Function that recieves a handleClose function and should render the modal content.
*/
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@
"@babel/core": "^7.4.3",
"@commitlint/cli": "^7.1.2",
"@commitlint/config-conventional": "^7.1.2",
"@storybook/addon-a11y": "^5.0.6",
"@storybook/addons": "^5.0.6",
"@storybook/react": "^5.0.6",
"@storybook/storybook-deployer": "^2.8.1",
"@types/jest": "^24.0.11",
Expand Down
27 changes: 20 additions & 7 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@ import { focusFirstNode, handleTabPress } from './focus';
import ModalPortal, { PortalBaseProps } from './Portal';
import { CONTAINER_BASE_STYLE, OVERLAY_BASE_STYLE } from './styles';

const initialState = Object.freeze({
open: false,
isClosing: false
});
type State = {
isClosing: boolean;
open: boolean;
};

type State = typeof initialState;
function getInitialState(props: Props): State {
return {
isClosing: false,
open: props.initiallyOpen || false
};
}

type OptionalProps = {
closeOnEscapeClick?: boolean;
Expand All @@ -20,6 +25,7 @@ type OptionalProps = {
closeOnAnimationEnd?: boolean;
modalClassName?: string;
modalOverlayClassName?: string;
initiallyOpen?: boolean;
};

interface Props extends PortalBaseProps, OptionalProps {
Expand All @@ -40,7 +46,7 @@ function getLastOpenContainer(): React.RefObject<HTMLDivElement> {
}

class MicroModal extends React.PureComponent<Props, State> {
readonly state: State = initialState;
readonly state: State = getInitialState(this.props);

static defaultProps: OptionalProps = {
disableFocus: false,
Expand All @@ -50,9 +56,16 @@ class MicroModal extends React.PureComponent<Props, State> {
closeOnOverlayClick: true,
closeOnAnimationEnd: false,
modalOverlayClassName: '',
modalClassName: ''
modalClassName: '',
initiallyOpen: false
};

componentDidMount() {
if (this.props.initiallyOpen && !this.props.disableFocus) {
this.focusFirstNode();
}
}

isControlled = this.props.open !== undefined;
modalRef = React.createRef<HTMLDivElement>();
containerRef = React.createRef<HTMLDivElement>();
Expand Down
2 changes: 2 additions & 0 deletions stories/Uncontrolled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { StoryUncontrolledModal } from './components';

storiesOf('react-micro-modal', module)
.add('Default', () => <StoryUncontrolledModal />)
.add('Initially open', () => <StoryUncontrolledModal initiallyOpen={true} />)
.add('Modal closing animation', () => (
<StoryUncontrolledModal closeOnAnimationEnd={true} />
))
Expand All @@ -17,6 +18,7 @@ storiesOf('react-micro-modal', module)
.add('Disable focusing first element on afterOpen', () => (
<StoryUncontrolledModal disableFocus={true} />
))

.add('Custom className', () => (
<StoryUncontrolledModal
modalOverlayClassName="background--red"
Expand Down
1 change: 1 addition & 0 deletions stories/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ type StoryModalProps = {
closeOnAnimationEnd?: boolean;
modalClassName?: string;
modalOverlayClassName?: string;
initiallyOpen?: boolean;
};

const StoryUncontrolledModal = (props: StoryModalProps) => (
Expand Down
15 changes: 9 additions & 6 deletions test/__helpers__/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@ type TestProps = {
modalClassName?: string;
modalOverlayClassName?: string;
closeOnAnimationEnd?: boolean;
initiallyOpen?: boolean;
open?: boolean;
};

const UncontrolledTestModal = ({
closeOnEscapeClick = true,
closeOnOverlayClick = true,
modalClassName = '',
modalOverlayClassName = '',
closeOnAnimationEnd = false
closeOnAnimationEnd = false,
initiallyOpen = false,
open
}: TestProps) => {
return (
<Modal
Expand All @@ -28,6 +32,8 @@ const UncontrolledTestModal = ({
modalClassName={modalClassName}
closeOnEscapeClick={closeOnEscapeClick}
closeOnOverlayClick={closeOnOverlayClick}
initiallyOpen={initiallyOpen}
open={open}
trigger={handleOpen => (
<button onClick={handleOpen}>{openModalTriggerText}</button>
)}
Expand All @@ -37,18 +43,14 @@ const UncontrolledTestModal = ({
);
};

interface ControlledTestProps extends TestProps {
initiallyOpen?: boolean;
}

const ControlledTestModal = ({
initiallyOpen = false,
closeOnEscapeClick = true,
closeOnOverlayClick = true,
modalClassName = '',
modalOverlayClassName = '',
closeOnAnimationEnd = false
}: ControlledTestProps) => {
}: TestProps) => {
const [open, setOpen] = useState(initiallyOpen);
return (
<div>
Expand All @@ -59,6 +61,7 @@ const ControlledTestModal = ({
modalOverlayClassName={modalOverlayClassName}
closeOnEscapeClick={closeOnEscapeClick}
closeOnOverlayClick={closeOnOverlayClick}
initiallyOpen={initiallyOpen}
open={open}
handleClose={() => setOpen(false)}
>
Expand Down
23 changes: 13 additions & 10 deletions test/react-micro-modal.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ describe('Micro modal', () => {
);
});

it('Should be initially open with first element focues', () => {
shouldBeInitiallyOpenWithFocuesElement(
render(<ModalComponent initiallyOpen={true} />)
);
});

it('Should be able to apply custom className to modal', () => {
shouldBeAbleToApplyCustomClassName(
render(
Expand All @@ -139,18 +145,15 @@ describe('Micro modal', () => {
});
});
});

describe('Controlled modal', () => {
it('Should be initially open on open prop passed', () => {
const { getByTestId } = render(
<ControlledTestModal initiallyOpen={true} />
);
const modalWrapper = getByTestId('micro-modal');
expectModalIsOpen(modalWrapper);
});
});
});

function shouldBeInitiallyOpenWithFocuesElement(renderResult: RenderResult) {
const { getByTestId, getByText } = renderResult;
let modalWrapper = getByTestId('micro-modal');
expectModalIsOpen(modalWrapper);
expect(getByText(firstFocusableElementText)).toBe(document.activeElement);
}

function shouldCloseAfterClosingAnimationEnds(renderResult: RenderResult) {
const { getByTestId, getByText } = renderResult;
let modalWrapper = getByTestId('micro-modal');
Expand Down
49 changes: 47 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1464,7 +1464,47 @@
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==

"@storybook/addons@5.0.6":
"@storybook/addon-a11y@^5.0.6":
version "5.0.6"
resolved "https://registry.yarnpkg.com/@storybook/addon-a11y/-/addon-a11y-5.0.6.tgz#ba8ee8041626341047cdcfbf07bbc51627839ae3"
integrity sha512-X7PCBXrzJuAllNzGtF56JZu/tK+posJW+jKm+a0eg53C/DUY6ugaMv1lsooFthoIUrkhQZuVTesHluUIvSeM7g==
dependencies:
"@storybook/addons" "5.0.6"
"@storybook/client-logger" "5.0.6"
"@storybook/components" "5.0.6"
"@storybook/core-events" "5.0.6"
"@storybook/theming" "5.0.6"
axe-core "^3.1.2"
common-tags "^1.8.0"
core-js "^2.6.5"
global "^4.3.2"
memoizerific "^1.11.3"
prop-types "^15.6.2"
react "^16.8.1"
react-dom "^16.8.1"
util-deprecate "^1.0.2"

"@storybook/addon-actions@^5.0.6":
version "5.0.6"
resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-5.0.6.tgz#78cf9c05b8b6cde1d5d46961ffb95d92be16bf46"
integrity sha512-Qbenp6Dv8uphDKsIemtW8bSu3PNYIDSB5av7bW9GieYex8QL6ntv7dja0DaD6NmIfjOZdO0Q0U3Nc9CAgHGOBQ==
dependencies:
"@storybook/addons" "5.0.6"
"@storybook/components" "5.0.6"
"@storybook/core-events" "5.0.6"
"@storybook/theming" "5.0.6"
core-js "^2.6.5"
fast-deep-equal "^2.0.1"
global "^4.3.2"
lodash "^4.17.11"
make-error "^1.3.5"
polished "^2.3.3"
prop-types "^15.6.2"
react "^16.8.1"
react-inspector "^2.3.0"
uuid "^3.3.2"

"@storybook/addons@5.0.6", "@storybook/addons@^5.0.6":
version "5.0.6"
resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-5.0.6.tgz#3ecb7fd8421e6557b1abc71ebede905e8decfebc"
integrity sha512-VZFYnckWRErkOTZG3a3y7jvnPUjOsTCbWSxvJVMM5SfPzlZbcUz16wJDCJ/pQWI0VzhHFEAVfae9DcuiF031Ow==
Expand Down Expand Up @@ -2648,6 +2688,11 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==

axe-core@^3.1.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.2.2.tgz#b06d6e9ae4636d706068843272bfaeed3fe97362"
integrity sha512-gAy4kMSPpuRJV3mwictJqlg5LhE84Vw2CydKdC4tvrLhR6+G3KW51zbL/vYujcLA2jvWOq3HMHrVeNuw+mrLVA==

babel-code-frame@^6.22.0, babel-code-frame@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
Expand Down Expand Up @@ -8428,7 +8473,7 @@ make-dir@^2.0.0:
pify "^4.0.1"
semver "^5.6.0"

make-error@1.x, make-error@^1.1.1:
make-error@1.x, make-error@^1.1.1, make-error@^1.3.5:
version "1.3.5"
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8"
integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==
Expand Down

0 comments on commit 98e64a8

Please sign in to comment.