Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@hig/banner

Package Overview
Dependencies
Maintainers
5
Versions
284
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@hig/banner - npm Package Compare versions

Comparing version 0.2.0-alpha.8d7714a2 to 0.2.0-alpha.8e499225

.babelrc

26

package.json
{
"name": "@hig/banner",
"version": "0.2.0-alpha.8d7714a2",
"version": "0.2.0-alpha.8e499225",
"description": "HIG Banner",

@@ -11,4 +11,6 @@ "author": "Autodesk Inc.",

"devDependencies": {
"@hig/scripts": "0.2.0-alpha.8d7714a2",
"@hig/styles": "0.1.0-alpha.8d7714a2"
"@hig/eslint-config": "0.2.0-alpha.8e499225",
"@hig/scripts": "0.2.0-alpha.8e499225",
"@hig/styles": "0.1.0-alpha.8e499225",
"hig-react": "0.29.0-alpha.8e499225"
},

@@ -20,7 +22,19 @@ "peerDependencies": {

"scripts": {
"build": "hig-scripts-build"
"build": "hig-scripts-build",
"lint": "eslint ./src/**/*"
},
"dependencies": {
"hig-react": "0.29.0-alpha.8d7714a2"
}
"@hig/icon": "0.1.0-alpha.8e499225",
"@hig/icon-button": "0.1.0-alpha.8e499225",
"@hig/icons": "0.1.0-alpha.8e499225",
"@hig/typography": "0.1.0-alpha.8e499225",
"react-lifecycles-compat": "^2.0.0"
},
"eslintConfig": {
"extends": "@hig"
},
"eslintIgnore": [
"*.scss",
"*.json"
]
}

@@ -1,88 +0,21 @@

import React from "react";
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import { text, select } from "@storybook/addon-knobs/react";
import { withInfo } from "@storybook/addon-info";
import { Button } from "hig-react";
import Banner from "../Banner";
import { selectLanguage } from "./getKnobs";
import infoOptions from "./infoOptions";
import renderBannerStory from "./renderBannerStory";
import stories from "./stories";
function getBannerKnobs(props) {
const {
type,
placement,
label,
message,
dismissButtonTitle,
onDismiss,
...otherProps
} = props;
const storybook = storiesOf("Banner", module);
return {
type: select("Type", Banner.AVAILABLE_TYPES, type),
placement: select("Placement", Banner.AVAILABLE_PLACEMENTS, placement),
label: text("Label", label),
message: text("Message", message),
dismissButtonTitle: text("Dismiss title", dismissButtonTitle),
onDismiss: action("Banner dismissed", onDismiss),
labelId: "unique-id",
isVisible: true,
...otherProps
};
}
stories.forEach(({ description, getProps }) => {
storybook.add(
description,
withInfo(infoOptions)(() => {
const language = selectLanguage();
const props = getProps({ language });
function BannerDemo({ children, ...props }) {
return (
<div style={{ marginBottom: "15px" }}>
<Banner {...getBannerKnobs(props)}>{children}</Banner>
</div>
return renderBannerStory(props);
})
);
}
function BannerStory({ props }) {
return <BannerDemo {...props} />;
}
const bannerStories = storiesOf("Banner", module);
const stories = [
{
description: "default",
props: {}
},
{
description: "verbose, with interactions",
props: {
type: Banner.types.WARNING,
label: "PROCESS COMPLETE",
// eslint-disable-next-line max-len
message:
"Changes have been made to you document. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam.",
/** @todo Cleanup/refactor */
children: ({ isWrappingActions }) => (
<Banner.Interactions isWrappingActions={isWrappingActions}>
<Banner.Action>
<Button
type="secondary"
size="small"
width={isWrappingActions ? "grow" : "shrink"}
title={text("Resolve text", "Accept Changes")}
/>
</Banner.Action>
<Banner.Action>
<Button
type="secondary"
size="small"
width={isWrappingActions ? "grow" : "shrink"}
title={text("Reject text", "Reject Changes")}
/>
</Banner.Action>
</Banner.Interactions>
)
}
}
];
stories.forEach(({ description, props }) => {
bannerStories.add(description, () => <BannerStory props={props} />);
});

@@ -6,2 +6,3 @@ import React, { Component } from "react";

import { types, AVAILABLE_TYPES } from "./types";
import animatorPropsByPlacement from "./animatorPropsByPlacement";
import BannerAction from "./BannerAction";

@@ -18,6 +19,6 @@ import BannerAnimator from "./BannerAnimator";

* @property {string} [label]
* @property {string} [message]
* @property {string} [labelledBy]
* @property {any} [actions]
* @property {string} [dismissButtonTitle]
* @property {Function} [onDismiss]
* @property {string} [labelId]
* @property {boolean} [isVisible]

@@ -45,4 +46,6 @@ * @property {any} [children]

label: PropTypes.string,
/** The displayed message */
message: PropTypes.string,
/** The ID used for ARIA labeling */
labelledBy: PropTypes.string,
/** Banner actions; Any JSX, or a render prop function */
actions: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
/** Accessibility text for the dismiss button */

@@ -52,8 +55,6 @@ dismissButtonTitle: PropTypes.string,

onDismiss: PropTypes.func,
/** The ID used for ARIA labeling */
labelId: PropTypes.string,
/** Animation; Determines the visibility of the banner */
isVisible: PropTypes.bool,
/** Banner actions; Any JSX, or a render prop function */
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func])
/** The displayed message */
children: PropTypes.string
};

@@ -74,9 +75,26 @@

render() {
const { isVisible, children: actions } = this.props;
/**
* @param {import("./BannerAnimator").ContainerBag} containerBag
*/
renderContainer = ({ handleReady }) => {
const { actions } = this.props;
const { renderPresenter } = this;
return (
<BannerAnimator isVisible={isVisible}>
<BannerContainer actions={actions}>{renderPresenter}</BannerContainer>
<BannerContainer actions={actions} onReady={handleReady}>
{renderPresenter}
</BannerContainer>
);
};
render() {
const { isVisible, placement } = this.props;
const { renderContainer } = this;
return (
<BannerAnimator
isVisible={isVisible}
{...animatorPropsByPlacement[placement]}
>
{renderContainer}
</BannerAnimator>

@@ -83,0 +101,0 @@ );

import { mount } from "enzyme";
import React from "react";
import renderer from "react-test-renderer";

@@ -7,47 +8,52 @@ import Banner from "./Banner";

describe("banner/Banner", () => {
describe("Action", () => {
it("exposes the `Action` component", () => {
expect(Banner).toHaveProperty("Action", expect.any(Function));
});
});
const exposedComponents = ["Action", "Interactions", "Presenter"];
const componentMatcher = expect.any(Function);
describe("Interactions", () => {
it("exposes the `Interactions` component", () => {
expect(Banner).toHaveProperty("Interactions", expect.any(Function));
exposedComponents.forEach(componentName => {
describe(componentName, () => {
it(`exposes the \`${componentName}\` component`, () => {
expect(Banner).toHaveProperty(componentName, componentMatcher);
});
});
});
describe("Presenter", () => {
it("exposes the `Presenter` component", () => {
expect(Banner).toHaveProperty("Presenter", expect.any(Function));
});
});
describe("sub-component rendering", () => {
const renderedComponents = [
"BannerAnimator",
"BannerContainer",
"BannerPresenter"
];
describe("rendering", () => {
beforeAll(() => {
window.requestAnimationFrame = jest.fn();
});
let wrapper;
afterAll(() => {
delete window.requestAnimationFrame;
beforeEach(() => {
wrapper = mount(<Banner />);
});
it("renders the `BannerAnimator`", () => {
const wrapper = mount(<Banner />);
expect(wrapper.find("BannerAnimator")).toBePresent();
renderedComponents.forEach(componentName => {
describe(componentName, () => {
it(`renders a \`${componentName}\` component`, () => {
expect(wrapper.find(componentName)).toBePresent();
});
});
});
});
it("renders the `BannerContainer`", () => {
const wrapper = mount(<Banner />);
describe("snapshot tests", () => {
const cases = [
{
description: "renders with no props",
props: {}
}
];
expect(wrapper.find("BannerContainer")).toBePresent();
});
cases.forEach(({ description, props: { children, ...otherProps } }) => {
it(description, () => {
const wrapper = <Banner {...otherProps}>{children}</Banner>;
const tree = renderer.create(wrapper).toJSON();
it("renders the `BannerPresenter`", () => {
const wrapper = mount(<Banner />);
expect(wrapper.find("BannerPresenter")).toBePresent();
expect(tree).toMatchSnapshot();
});
});
});
});

@@ -16,2 +16,3 @@ import { Component } from "react";

* @property {any} [actions]
* @property {Function} [onReady]
* @property {function(PresenterBag): any} [children]

@@ -38,2 +39,4 @@ */

actions: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
/** Called after the component has been mounted, and dynamically resized */
onReady: PropTypes.func,
/** A render prop function to render a `BannerPresenter` */

@@ -51,3 +54,3 @@ children: PropTypes.func.isRequired

this.bindResize();
this.updateWrapping();
this.updateWrapping(this.props.onReady);
}

@@ -141,3 +144,6 @@

updateContentWrapping = () => {
/**
* @param {Function} callback
*/
updateContentWrapping = callback => {
const update = { isWrappingContent: this.shouldWrapContent() };

@@ -147,12 +153,17 @@

delete this.wrappingFrame;
if (callback) callback();
});
};
updateActionWrapping = () => {
/**
* @param {Function} callback
*/
updateActionWrapping = callback => {
const update = { isWrappingActions: this.shouldWrapActions() };
this.setState(update, () => {
this.wrappingFrame = window.requestAnimationFrame(
this.updateContentWrapping
);
this.wrappingFrame = window.requestAnimationFrame(() => {
this.updateContentWrapping(callback);
});
});

@@ -163,9 +174,10 @@ };

* Asynchronously updates the wrapping behavior of the presenter
* @param {Function} callback
*/
updateWrapping() {
updateWrapping(callback) {
if (this.wrappingFrame !== undefined) return;
this.wrappingFrame = window.requestAnimationFrame(
this.updateActionWrapping
);
this.wrappingFrame = window.requestAnimationFrame(() => {
this.updateActionWrapping(callback);
});
}

@@ -199,3 +211,3 @@

refInteractionsWrapper,
children: this.renderActions()
actions: this.renderActions()
};

@@ -202,0 +214,0 @@ }

@@ -28,3 +28,3 @@ import { shallow } from "enzyme";

expect(presenterBag).toMatchObject({
children: "foobar",
actions: "foobar",
isWrappingContent: false,

@@ -31,0 +31,0 @@ refContent: expect.any(Function),

@@ -5,3 +5,2 @@ import React from "react";

import { types, AVAILABLE_TYPES } from "../types";
import { placements, AVAILABLE_PLACEMENTS } from "../placements";

@@ -13,3 +12,2 @@ import {

InteractionsWrapper,
Label,
Message,

@@ -23,8 +21,7 @@ Notification,

* @property {string} [type]
* @property {string} [placement]
* @property {string} [label]
* @property {string} [message]
* @property {string} [labelledBy]
* @property {any} [actions]
* @property {string} [dismissButtonTitle]
* @property {Function} [onDismiss]
* @property {string} [labelId]
* @property {boolean} [isWrappingContent]

@@ -45,8 +42,7 @@ * @property {function(HTMLDivElement): any} [refContent]

type,
placement,
label,
message,
labelledBy,
actions,
dismissButtonTitle,
onDismiss,
labelId,
isWrappingContent,

@@ -56,8 +52,6 @@ refContent,

refInteractionsWrapper,
children
children: message
} = props;
const hasLabel = !!label;
const hasActions = React.Children.count(children) > 0;
const wrapperLabelledBy = hasLabel ? labelId : undefined;
const hasActions = React.Children.count(actions) > 0;

@@ -67,6 +61,6 @@ return (

type={type}
placement={placement}
hasActions={hasActions}
isWrappingContent={isWrappingContent}
labelledBy={wrapperLabelledBy}
label={label}
labelledBy={labelledBy}
>

@@ -76,3 +70,2 @@ <Icon type={type} />

<Notification innerRef={refNotification}>
{hasLabel ? <Label id={labelId}>{label}</Label> : null}
<Message>{message}</Message>

@@ -82,3 +75,3 @@ </Notification>

<InteractionsWrapper innerRef={refInteractionsWrapper}>
{children}
{actions}
</InteractionsWrapper>

@@ -95,6 +88,5 @@ ) : null}

type: types.PRIMARY,
placement: placements.STANDARD,
message: "Message",
dismissButtonTitle: "Dismiss",
isWrappingContent: false
isWrappingContent: false,
children: "Message"
};

@@ -105,8 +97,8 @@

type: PropTypes.oneOf(AVAILABLE_TYPES),
/** Determines the intended placement of banner */
placement: PropTypes.oneOf(AVAILABLE_PLACEMENTS),
/** The label of the message displayed */
label: PropTypes.string,
/** The displayed message */
message: PropTypes.string,
/** The ID used for ARIA labeling */
labelledBy: PropTypes.string,
/** Banner actions */
actions: PropTypes.node,
/** Accessibility text for the dismiss button */

@@ -116,4 +108,2 @@ dismissButtonTitle: PropTypes.string,

onDismiss: PropTypes.func,
/** The ID used for ARIA labeling */
labelId: PropTypes.string,
/** Determines whether the banner content wraps */

@@ -127,4 +117,4 @@ isWrappingContent: PropTypes.bool,

refInteractionsWrapper: PropTypes.func,
/** Banner actions */
/** The displayed message */
children: PropTypes.node
};
import React from "react";
import renderer from "react-test-renderer";
import { placements } from "../placements";
import { types } from "../types";
import BannerPresenter from "./BannerPresenter";
describe.only("banner/BannerPresenter/BannerPresenter", () => {
describe("banner/BannerPresenter/BannerPresenter", () => {
[

@@ -19,23 +18,22 @@ {

label: "HELLO",
message: "World"
children: "World"
}
},
{
description: "renders with a string as children",
description: "renders with a string as actions",
props: {
placement: placements.TOP,
children: "foobar"
actions: "foobar"
}
},
{
description: "renders with a node as children",
description: "renders with a node as actions",
props: {
dismissButtonTitle: "boom",
children: <span>foo</span>
actions: <span>foo</span>
}
},
{
description: "renders with a fragment as children",
description: "renders with a fragment as actions",
props: {
children: [<span key="0">bar</span>, <div key="1">baz</div>]
actions: [<span key="0">bar</span>, <div key="1">baz</div>]
}

@@ -42,0 +40,0 @@ }

@@ -6,6 +6,7 @@ /* eslint-disable react/prop-types */

import { Icon as BasicIcon, IconButton, Text } from "hig-react";
import BasicIcon, { names as iconNames, sizes as iconSizes } from "@hig/icon";
import IconButton, { types as iconButtonTypes } from "@hig/icon-button";
import { Text } from "@hig/typography";
import "./banner-presenter.scss";
import { placements } from "../placements";
import { types } from "../types";

@@ -16,5 +17,2 @@

const { types: iconButtonTypes } = IconButton;
const { names: iconNames, sizes: iconSizes } = BasicIcon;
/** @type {Object.<string, string>} */

@@ -28,3 +26,3 @@ const classNames = Object.freeze({

interactionsWrapper: "hig__banner__interactions-wrapper",
notification: "hig__banner__notification",
notification: "hig__banner__message",
wrapper: "hig__banner",

@@ -42,8 +40,2 @@ wrapperBottom: "hig__banner--bottom",

/** @type {Object.<string, string>} */
const wrapperModifiersByPlacement = {
[placements.BOTTOM]: classNames.wrapperBottom,
[placements.TOP]: classNames.wrapperTop
};
/** @type {Object.<string, string>} */
const wrapperModifiersByType = {

@@ -73,3 +65,2 @@ [types.PRIMARY]: classNames.wrapperPrimary,

* @property {string} type
* @property {string} placement
* @property {boolean} hasActions

@@ -88,4 +79,4 @@ * @property {string | undefined} [labelledBy]

type,
placement,
hasActions,
label,
labelledBy,

@@ -99,3 +90,2 @@ isWrappingContent,

wrapperModifiersByType[type],
wrapperModifiersByPlacement[placement],
hasActions ? classNames.wrapperInteractive : undefined,

@@ -106,3 +96,9 @@ isWrappingContent ? classNames.wrapperWrapContent : undefined

return (
<div role="alert" aria-labelledby={labelledBy} className={classes}>
<div
role="alert"
aria-label={label}
aria-labelledby={labelledBy}
aria-live={type === types.URGENT ? "assertive" : "polite"}
className={classes}
>
{children}

@@ -179,16 +175,2 @@ </div>

/**
* @typedef {Object} LabelProps
* @property {string} [id]
* @property {any} [children]
*/
/**
* @param {LabelProps} props
* @returns {JSX.Element}
*/
export function Label({ id, children }) {
return <Text color={TEXT_COLOR} id={id}>{`${children}: `}</Text>;
}
/**
* @param {StyledProps} props

@@ -202,6 +184,2 @@ * @returns {JSX.Element}

if (typeof children === "function") {
return children();
}
return children;

@@ -208,0 +186,0 @@ }

import "./external.scss";
export { default } from "./Banner";

@@ -3,0 +4,0 @@ export { default as BannerAction } from "./BannerAction";

/** @type {Object.<string, string>} */
export const placements = Object.freeze({
STANDARD: "standard",
TOP: "top",
BOTTOM: "bottom"
ABOVE: "above",
ABOVE_OVERLAY: "above-overlay",
BETWEEN: "between",
BELOW_OVERLAY: "below-overlay"
});

@@ -7,0 +8,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc