diff --git a/src/core/client/admin/components/App.spec.tsx b/src/core/client/admin/components/App.spec.tsx index 5331f9cb8..40a55c97e 100644 --- a/src/core/client/admin/components/App.spec.tsx +++ b/src/core/client/admin/components/App.spec.tsx @@ -1,16 +1,20 @@ import React from "react"; import { createRenderer } from "react-test-renderer/shallow"; +import { removeFragmentRefs } from "talk-framework/testHelpers"; import { PropTypesOf } from "talk-framework/types"; import App from "./App"; +const AppN = removeFragmentRefs(App); + it("renders correctly", () => { - const props: PropTypesOf = { + const props: PropTypesOf = { + me: {}, children: "child", }; const renderer = createRenderer(); - renderer.render(); + renderer.render(); expect(renderer.getRenderOutput()).toMatchSnapshot(); }); diff --git a/src/core/client/admin/components/App.tsx b/src/core/client/admin/components/App.tsx index f04127343..048c9cc1d 100644 --- a/src/core/client/admin/components/App.tsx +++ b/src/core/client/admin/components/App.tsx @@ -1,16 +1,22 @@ import React, { StatelessComponent } from "react"; +import { PropTypesOf } from "talk-framework/types"; import { Logo } from "talk-ui/components"; import { AppBar, Begin, Divider, End } from "talk-ui/components/AppBar"; -import SignOutButtonContainer from "../containers/SignOutButtonContainer"; +import UserMenuContainer from "../containers/UserMenuContainer"; import DecisionHistoryButton from "./DecisionHistoryButton"; import Navigation from "./Navigation"; import Version from "./Version"; import styles from "./App.css"; -const App: StatelessComponent = ({ children }) => ( +interface Props { + me: PropTypesOf["me"]; + children: React.ReactNode; +} + +const App: StatelessComponent = ({ children, me }) => (
@@ -23,7 +29,7 @@ const App: StatelessComponent = ({ children }) => ( - + {children} diff --git a/src/core/client/admin/components/DecisionHistoryButton.tsx b/src/core/client/admin/components/DecisionHistoryButton.tsx index a6f5021b1..27007ff05 100644 --- a/src/core/client/admin/components/DecisionHistoryButton.tsx +++ b/src/core/client/admin/components/DecisionHistoryButton.tsx @@ -1,56 +1,43 @@ -import React from "react"; +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent } from "react"; -import { oncePerFrame } from "talk-common/utils"; import { BaseButton, ClickOutside, Icon, Popover } from "talk-ui/components"; import DecisionHistoryQuery from "../views/decisionHistory/queries/DecisionHistoryQuery"; import styles from "./DecisionHistoryButton.css"; -class DecisionHistoryButton extends React.Component { - // Helper that prevents calling toggleVisibility more then once per frame. - // In essence this means we'll process an event only once. - // This might happen, when clicking on the button which will - // cause its onClick to happen as well as onClickOutside. - private toggleVisibilityOncePerFrame = oncePerFrame( - (toggleVisibility: () => void) => toggleVisibility() - ); +const popoverID = "decision-history-popover"; - public render() { - const popoverID = `decision-history-popover`; - return ( - { - const hide = () => - this.toggleVisibilityOncePerFrame(toggleVisibility); - return ( - -
- -
-
- ); - }} - > - {({ toggleVisibility, forwardRef, visible }) => ( - this.toggleVisibilityOncePerFrame(toggleVisibility)} - aria-controls={popoverID} - ref={forwardRef} - className={styles.historyIcon} - data-testid="decisionHistory-toggle" - > - history - - )} -
- ); - } -} +const DecisionHistoryButton: StatelessComponent = () => ( + + ( + +
+ +
+
+ )} + > + {({ toggleVisibility, ref, visible }) => ( + + history + + )} +
+
+); export default DecisionHistoryButton; diff --git a/src/core/client/admin/components/SignOutButton.css b/src/core/client/admin/components/SignOutButton.css deleted file mode 100644 index 8e62c2bc6..000000000 --- a/src/core/client/admin/components/SignOutButton.css +++ /dev/null @@ -1,5 +0,0 @@ -.root { - font-weight: var(--font-weight-bold); - font-size: calc(18rem / var(--rem-base)); - letter-spacing: calc(-0.2em / 18); -} diff --git a/src/core/client/admin/components/SignOutButton.tsx b/src/core/client/admin/components/SignOutButton.tsx deleted file mode 100644 index e50927d72..000000000 --- a/src/core/client/admin/components/SignOutButton.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Localized } from "fluent-react/compat"; -import React, { StatelessComponent } from "react"; - -import { Button } from "talk-ui/components"; - -import styles from "./SignOutButton.css"; - -interface Props { - id?: string; - onClick: React.EventHandler; -} - -const SignOutButton: StatelessComponent = props => ( - - - -); - -export default SignOutButton; diff --git a/src/core/client/admin/components/UserMenu.css b/src/core/client/admin/components/UserMenu.css new file mode 100644 index 000000000..278978da0 --- /dev/null +++ b/src/core/client/admin/components/UserMenu.css @@ -0,0 +1,11 @@ +.button { + padding-top: 0; + padding-bottom: 0; + margin-left: 5px; +} +.buttonText { + font-size: calc(18rem / var(--rem-base)); + letter-spacing: calc(-0.2em / 18); + margin-left: 2px; + margin-right: 1px; +} diff --git a/src/core/client/admin/components/SignOutButton.spec.tsx b/src/core/client/admin/components/UserMenu.spec.tsx similarity index 63% rename from src/core/client/admin/components/SignOutButton.spec.tsx rename to src/core/client/admin/components/UserMenu.spec.tsx index 91d761df7..b92c27351 100644 --- a/src/core/client/admin/components/SignOutButton.spec.tsx +++ b/src/core/client/admin/components/UserMenu.spec.tsx @@ -4,14 +4,14 @@ import { createRenderer } from "react-test-renderer/shallow"; import { PropTypesOf } from "talk-framework/types"; -import SignOutButton from "./SignOutButton"; +import UserMenu from "./UserMenu"; it("renders correctly", () => { - const props: PropTypesOf = { - id: "id", - onClick: noop, + const props: PropTypesOf = { + username: "Admin", + onSignOut: noop as any, }; const renderer = createRenderer(); - renderer.render(); + renderer.render(); expect(renderer.getRenderOutput()).toMatchSnapshot(); }); diff --git a/src/core/client/admin/components/UserMenu.tsx b/src/core/client/admin/components/UserMenu.tsx new file mode 100644 index 000000000..96c5daef4 --- /dev/null +++ b/src/core/client/admin/components/UserMenu.tsx @@ -0,0 +1,79 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent } from "react"; + +import { + Button, + ButtonIcon, + ClickOutside, + Dropdown, + DropdownButton, + DropdownDivider, + Popover, +} from "talk-ui/components"; + +import styles from "./UserMenu.css"; + +interface Props { + username: string; + onSignOut: React.EventHandler; +} + +const UserMenu: StatelessComponent = props => ( + + ( + + + + + View Latest Release + + + + + Report a Bug or Give Feedback + + + + + + Sign Out + + + + + )} + > + {({ toggleVisibility, ref, visible }) => ( + + )} + + +); + +export default UserMenu; diff --git a/src/core/client/admin/components/__snapshots__/App.spec.tsx.snap b/src/core/client/admin/components/__snapshots__/App.spec.tsx.snap index 32304ba1a..eb52415e7 100644 --- a/src/core/client/admin/components/__snapshots__/App.spec.tsx.snap +++ b/src/core/client/admin/components/__snapshots__/App.spec.tsx.snap @@ -22,8 +22,8 @@ exports[`renders correctly 1`] = ` - diff --git a/src/core/client/admin/components/__snapshots__/SignOutButton.spec.tsx.snap b/src/core/client/admin/components/__snapshots__/SignOutButton.spec.tsx.snap deleted file mode 100644 index 31e45708a..000000000 --- a/src/core/client/admin/components/__snapshots__/SignOutButton.spec.tsx.snap +++ /dev/null @@ -1,15 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders correctly 1`] = ` - - - Sign Out - - -`; diff --git a/src/core/client/admin/components/__snapshots__/UserMenu.spec.tsx.snap b/src/core/client/admin/components/__snapshots__/UserMenu.spec.tsx.snap new file mode 100644 index 000000000..ba453ecd6 --- /dev/null +++ b/src/core/client/admin/components/__snapshots__/UserMenu.spec.tsx.snap @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` + + + [Function] + + +`; diff --git a/src/core/client/admin/containers/AppContainer.tsx b/src/core/client/admin/containers/AppContainer.tsx index 20b819fb4..ff29a6c5b 100644 --- a/src/core/client/admin/containers/AppContainer.tsx +++ b/src/core/client/admin/containers/AppContainer.tsx @@ -1,29 +1,34 @@ -import { BrowserProtocol, queryMiddleware } from "farce"; -import { createFarceRouter, createRender } from "found"; -import { Resolver } from "found-relay"; +import React from "react"; -import React, { StatelessComponent } from "react"; -import { TalkContextConsumer } from "talk-framework/lib/bootstrap/TalkContext"; -import routeConfig from "../routeConfig"; -import NotFound from "../routes/NotFound"; +import { AppContainerQueryResponse } from "talk-admin/__generated__/AppContainerQuery.graphql"; -const Router = createFarceRouter({ - historyProtocol: new BrowserProtocol(), - historyMiddlewares: [queryMiddleware], - routeConfig, - render: createRender({ - renderError: ({ error }) => ( -
{error.status === 404 ? : "Error"}
- ), - }), -}); +import { graphql } from "talk-framework/lib/relay"; +import { withRouteConfig } from "talk-framework/lib/router"; -const AppContainer: StatelessComponent = () => ( - - {({ relayEnvironment }) => ( - - )} - -); +import App from "../components/App"; -export default AppContainer; +interface Props { + data: AppContainerQueryResponse | null; +} + +class AppContainer extends React.Component { + public render() { + return ( + + {this.props.children} + + ); + } +} + +const enhanced = withRouteConfig({ + query: graphql` + query AppContainerQuery { + me { + ...UserMenuContainer_me + } + } + `, +})(AppContainer); + +export default enhanced; diff --git a/src/core/client/admin/containers/EntryContainer.tsx b/src/core/client/admin/containers/EntryContainer.tsx new file mode 100644 index 000000000..f04dd40c3 --- /dev/null +++ b/src/core/client/admin/containers/EntryContainer.tsx @@ -0,0 +1,29 @@ +import { BrowserProtocol, queryMiddleware } from "farce"; +import { createFarceRouter, createRender } from "found"; +import { Resolver } from "found-relay"; + +import React, { StatelessComponent } from "react"; +import { TalkContextConsumer } from "talk-framework/lib/bootstrap/TalkContext"; +import routeConfig from "../routeConfig"; +import NotFound from "../routes/NotFound"; + +const Router = createFarceRouter({ + historyProtocol: new BrowserProtocol(), + historyMiddlewares: [queryMiddleware], + routeConfig, + render: createRender({ + renderError: ({ error }) => ( +
{error.status === 404 ? : "Error"}
+ ), + }), +}); + +const EntryContainer: StatelessComponent = () => ( + + {({ relayEnvironment }) => ( + + )} + +); + +export default EntryContainer; diff --git a/src/core/client/admin/containers/SignOutButtonContainer.tsx b/src/core/client/admin/containers/SignOutButtonContainer.tsx deleted file mode 100644 index 226421ef0..000000000 --- a/src/core/client/admin/containers/SignOutButtonContainer.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from "react"; - -import { SignOutMutation, withSignOutMutation } from "talk-framework/mutations"; -import SignOutButton from "../components/SignOutButton"; - -interface Props { - signOut: SignOutMutation; - id?: string; -} - -class RedirectAppContainer extends React.Component { - private handleClick = () => this.props.signOut(); - public render() { - return ; - } -} - -const enhanced = withSignOutMutation(RedirectAppContainer); - -export default enhanced; diff --git a/src/core/client/admin/containers/UserMenuContainer.tsx b/src/core/client/admin/containers/UserMenuContainer.tsx new file mode 100644 index 000000000..134fae771 --- /dev/null +++ b/src/core/client/admin/containers/UserMenuContainer.tsx @@ -0,0 +1,36 @@ +import React from "react"; + +import { UserMenuContainer_me as MeData } from "talk-admin/__generated__/UserMenuContainer_me.graphql"; +import { graphql, withFragmentContainer } from "talk-framework/lib/relay"; +import { SignOutMutation, withSignOutMutation } from "talk-framework/mutations"; + +import UserMenu from "../components/UserMenu"; + +interface Props { + signOut: SignOutMutation; + me: MeData | null; +} + +class UserMenuContainer extends React.Component { + private handleSignOut = () => this.props.signOut(); + public render() { + return ( + + ); + } +} + +const enhanced = withSignOutMutation( + withFragmentContainer({ + me: graphql` + fragment UserMenuContainer_me on User { + username + } + `, + })(UserMenuContainer) +); + +export default enhanced; diff --git a/src/core/client/admin/index.tsx b/src/core/client/admin/index.tsx index b2e1f1772..0abff6332 100644 --- a/src/core/client/admin/index.tsx +++ b/src/core/client/admin/index.tsx @@ -2,7 +2,7 @@ import React, { StatelessComponent } from "react"; import ReactDOM from "react-dom"; import { createManaged } from "talk-framework/lib/bootstrap"; -import AppContainer from "./containers/AppContainer"; +import EntryContainer from "./containers/EntryContainer"; import { initLocalState } from "./local"; import localesData from "./locales"; @@ -15,7 +15,7 @@ async function main() { const Index: StatelessComponent = () => ( - + ); diff --git a/src/core/client/admin/routeConfig.tsx b/src/core/client/admin/routeConfig.tsx index 367fef205..f0ceae887 100644 --- a/src/core/client/admin/routeConfig.tsx +++ b/src/core/client/admin/routeConfig.tsx @@ -1,7 +1,7 @@ import { makeRouteConfig, Redirect, Route } from "found"; import React from "react"; -import App from "./components/App"; +import AppContainer from "./containers/AppContainer"; import AuthCheckContainer from "./containers/AuthCheckContainer"; import Community from "./routes/community/components/Community"; import ConfigureContainer from "./routes/configure/containers/ConfigureContainer"; @@ -25,7 +25,7 @@ import Stories from "./routes/stories/components/Stories"; export default makeRouteConfig( - + { .once() .returns({}); + const userMenu = await waitForElement(() => + within(testRenderer.root).getByText(users[0].username, { + selector: "button", + }) + ); + + userMenu.props.onClick(); + const signOutButton = await waitForElement(() => within(testRenderer.root).getByText("Sign Out") ); diff --git a/src/core/client/admin/test/create.tsx b/src/core/client/admin/test/create.tsx index c21de5b96..66b1a0a96 100644 --- a/src/core/client/admin/test/create.tsx +++ b/src/core/client/admin/test/create.tsx @@ -4,7 +4,7 @@ import React from "react"; import TestRenderer from "react-test-renderer"; import { Environment, RecordProxy, RecordSourceProxy } from "relay-runtime"; -import AppContainer from "talk-admin/containers/AppContainer"; +import EntryContainer from "talk-admin/containers/EntryContainer"; import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap"; import { PostMessageService } from "talk-framework/lib/postMessage"; import { RestClient } from "talk-framework/lib/rest"; @@ -54,7 +54,7 @@ export default function create(params: CreateParams) { const testRenderer = TestRenderer.create( - + , { createNodeMock } ); diff --git a/src/core/client/admin/test/decisionHistory/__snapshots__/decisionHistory.spec.tsx.snap b/src/core/client/admin/test/decisionHistory/__snapshots__/decisionHistory.spec.tsx.snap index 0e369f295..55ca11d5a 100644 --- a/src/core/client/admin/test/decisionHistory/__snapshots__/decisionHistory.spec.tsx.snap +++ b/src/core/client/admin/test/decisionHistory/__snapshots__/decisionHistory.spec.tsx.snap @@ -465,7 +465,7 @@ exports[`renders decision history popover button 1`] = ` className="AriaInfo-root" id="decision-history-popover-ariainfo" > - A dialog showing a permalink to the comment + A dialog showing the decision history
diff --git a/src/core/client/stream/tabs/comments/components/PermalinkButton/PermalinkButton.tsx b/src/core/client/stream/tabs/comments/components/PermalinkButton/PermalinkButton.tsx index 0afb1c38e..2317f10f5 100644 --- a/src/core/client/stream/tabs/comments/components/PermalinkButton/PermalinkButton.tsx +++ b/src/core/client/stream/tabs/comments/components/PermalinkButton/PermalinkButton.tsx @@ -1,7 +1,6 @@ import { Localized } from "fluent-react/compat"; -import React from "react"; +import React, { StatelessComponent } from "react"; -import { oncePerFrame } from "talk-common/utils"; import { Button, ButtonIcon, @@ -19,39 +18,26 @@ interface PermalinkProps { url: string; } -class Permalink extends React.Component { - // Helper that prevents calling toggleVisibility more then once per frame. - // In essence this means we'll process an event only once. - // This might happen, when clicking on the button which will - // cause its onClick to happen as well as onClickOutside. - private toggleVisibilityOncePerFrame = oncePerFrame( - (toggleVisibility: () => void) => toggleVisibility() - ); - - public render() { - const { commentID, url } = this.props; - const popoverID = `permalink-popover-${commentID}`; - return ( +const Permalink: StatelessComponent = ({ commentID, url }) => { + const popoverID = `permalink-popover-${commentID}`; + return ( + ( - - this.toggleVisibilityOncePerFrame(toggleVisibility) - } - > + )} > - {({ toggleVisibility, forwardRef, visible }) => ( + {({ toggleVisibility, ref, visible }) => ( )} - ); - } -} + + ); +}; export default Permalink; diff --git a/src/core/client/stream/tabs/comments/components/ReportButton/ReportButtonWithPopover.tsx b/src/core/client/stream/tabs/comments/components/ReportButton/ReportButtonWithPopover.tsx index 030d79017..036d0fee6 100644 --- a/src/core/client/stream/tabs/comments/components/ReportButton/ReportButtonWithPopover.tsx +++ b/src/core/client/stream/tabs/comments/components/ReportButton/ReportButtonWithPopover.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { oncePerFrame } from "talk-common/utils"; +import { Localized } from "fluent-react/compat"; import { ClickOutside, Popover } from "talk-ui/components"; import ReportCommentView from "../../views/reportComment"; @@ -13,53 +13,39 @@ interface Props { reported: boolean; } -class ReportButtonWithPopover extends React.Component { - // Helper that prevents calling toggleVisibility more then once per frame. - // In essence this means we'll process an event only once. - // This might happen, when clicking on the button which will - // cause its onClick to happen as well as onClickOutside. - private toggleVisibilityOncePerFrame = oncePerFrame( - (toggleVisibility: () => void) => toggleVisibility() - ); - - public render() { - const { comment, reported } = this.props; - const popoverID = `report-popover-${comment.id}`; - return ( +const ReportButtonWithPopover: React.StatelessComponent = ({ + comment, + reported, +}) => { + const popoverID = `report-popover-${comment.id}`; + return ( + ( - - this.toggleVisibilityOncePerFrame(toggleVisibility) - } - > + - this.toggleVisibilityOncePerFrame(toggleVisibility) - } + onClose={toggleVisibility} onResize={scheduleUpdate} /> )} > - {({ toggleVisibility, forwardRef, visible }) => ( + {({ toggleVisibility, ref, visible }) => ( - !reported && this.toggleVisibilityOncePerFrame(toggleVisibility) - } + onClick={evt => !reported && toggleVisibility(evt)} aria-controls={popoverID} - ref={forwardRef} + ref={ref} active={visible} reported={reported} /> )} - ); - } -} + + ); +}; export default ReportButtonWithPopover; diff --git a/src/core/client/ui/components/BaseButton/BaseButton.tsx b/src/core/client/ui/components/BaseButton/BaseButton.tsx index ba498ae49..9cdd40485 100644 --- a/src/core/client/ui/components/BaseButton/BaseButton.tsx +++ b/src/core/client/ui/components/BaseButton/BaseButton.tsx @@ -24,6 +24,7 @@ interface Props extends ButtonHTMLAttributes { anchor?: boolean; href?: string; target?: string; + rel?: string; /** * This prop can be used to add custom classnames. diff --git a/src/core/client/ui/components/Button/Button.tsx b/src/core/client/ui/components/Button/Button.tsx index 69e2d6f2b..acdcd2a78 100644 --- a/src/core/client/ui/components/Button/Button.tsx +++ b/src/core/client/ui/components/Button/Button.tsx @@ -9,8 +9,6 @@ import BaseButton, { BaseButtonProps } from "../BaseButton"; import styles from "./Button.css"; -// This should extend from BaseButton instead but we can't because of this bug -// TODO: add bug link. interface Props extends Omit { /** If set renders an anchor tag instead */ anchor?: boolean; diff --git a/src/core/client/ui/components/Dropdown/Button.css b/src/core/client/ui/components/Dropdown/Button.css new file mode 100644 index 000000000..3c8038320 --- /dev/null +++ b/src/core/client/ui/components/Dropdown/Button.css @@ -0,0 +1,27 @@ +.root { + composes: menuItem from "talk-ui/shared/typography.css"; + display: flex; + justify-content: space-between; + align-items: center; + padding: calc(0.5 * var(--spacing-unit)) var(--spacing-unit); + width: 100%; + text-align: left; +} + +.root:active { + background-color: var(--palette-grey-lighter); +} + +.mouseHover { + background-color: var(--palette-grey-lightest); +} + +.icon { + color: var(--palette-grey-main); + margin-left: var(--spacing-unit); + padding-top: 1px; +} + +.blankAdornment { + padding-right: calc(3.5 * var(--spacing-unit)); +} diff --git a/src/core/client/ui/components/Dropdown/Button.spec.tsx b/src/core/client/ui/components/Dropdown/Button.spec.tsx new file mode 100644 index 000000000..d05dc05fb --- /dev/null +++ b/src/core/client/ui/components/Dropdown/Button.spec.tsx @@ -0,0 +1,32 @@ +import { noop } from "lodash"; +import React from "react"; +import { createRenderer } from "react-test-renderer/shallow"; + +import { PropTypesOf } from "talk-framework/types"; + +import Button from "./Button"; + +it("renders anchor button", () => { + const props: PropTypesOf = { + href: "/moderate", + children: "link", + onClick: noop, + className: "custom", + target: "_blank", + }; + const renderer = createRenderer(); + renderer.render( + )} + + diff --git a/src/core/client/ui/components/Dropdown/Dropdown.spec.tsx b/src/core/client/ui/components/Dropdown/Dropdown.spec.tsx new file mode 100644 index 000000000..ca690c4ab --- /dev/null +++ b/src/core/client/ui/components/Dropdown/Dropdown.spec.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import { createRenderer } from "react-test-renderer/shallow"; + +import { PropTypesOf } from "talk-framework/types"; + +import Button from "./Button"; +import Divider from "./Divider"; +import Dropdown from "./Dropdown"; + +it("renders correctly", () => { + const props: PropTypesOf = { + children: ( + <> + + + + + + ), + className: "custom", + }; + const renderer = createRenderer(); + renderer.render(); + expect(renderer.getRenderOutput()).toMatchSnapshot(); +}); diff --git a/src/core/client/ui/components/Dropdown/Dropdown.tsx b/src/core/client/ui/components/Dropdown/Dropdown.tsx new file mode 100644 index 000000000..473d80ac8 --- /dev/null +++ b/src/core/client/ui/components/Dropdown/Dropdown.tsx @@ -0,0 +1,26 @@ +import React, { StatelessComponent } from "react"; + +import { withStyles } from "talk-ui/hocs"; + +import styles from "./Dropdown.css"; + +interface Props { + children?: React.ReactNode; + className?: string; + classes: typeof styles; +} + +const Dropdown: StatelessComponent = ({ + className, + children, + classes, + ...rest +}) => { + return ( +
+ {children} +
+ ); +}; + +export default withStyles(styles)(Dropdown); diff --git a/src/core/client/ui/components/Dropdown/__snapshots__/Button.spec.tsx.snap b/src/core/client/ui/components/Dropdown/__snapshots__/Button.spec.tsx.snap new file mode 100644 index 000000000..426a76ec5 --- /dev/null +++ b/src/core/client/ui/components/Dropdown/__snapshots__/Button.spec.tsx.snap @@ -0,0 +1,38 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders anchor button 1`] = ` + +`; + +exports[`renders button 1`] = ` + +`; diff --git a/src/core/client/ui/components/Dropdown/__snapshots__/Divider.spec.tsx.snap b/src/core/client/ui/components/Dropdown/__snapshots__/Divider.spec.tsx.snap new file mode 100644 index 000000000..722ea5f94 --- /dev/null +++ b/src/core/client/ui/components/Dropdown/__snapshots__/Divider.spec.tsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` + +`; diff --git a/src/core/client/ui/components/Dropdown/__snapshots__/Dropdown.spec.tsx.snap b/src/core/client/ui/components/Dropdown/__snapshots__/Dropdown.spec.tsx.snap new file mode 100644 index 000000000..9c171d0f0 --- /dev/null +++ b/src/core/client/ui/components/Dropdown/__snapshots__/Dropdown.spec.tsx.snap @@ -0,0 +1,32 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` + + + + Shortcuts + + + Report Bug + + + + Sign Out + + + +`; diff --git a/src/core/client/ui/components/Dropdown/index.ts b/src/core/client/ui/components/Dropdown/index.ts new file mode 100644 index 000000000..ff2882546 --- /dev/null +++ b/src/core/client/ui/components/Dropdown/index.ts @@ -0,0 +1,3 @@ +export { default as Dropdown } from "./Dropdown"; +export { default as Button } from "./Button"; +export { default as Divider } from "./Divider"; diff --git a/src/core/client/ui/components/FieldSet/FieldSet.mdx b/src/core/client/ui/components/FieldSet/FieldSet.mdx index 6f9ae741d..52fdd24fc 100644 --- a/src/core/client/ui/components/FieldSet/FieldSet.mdx +++ b/src/core/client/ui/components/FieldSet/FieldSet.mdx @@ -1,5 +1,5 @@ --- -name: AriaInfo +name: FieldSet menu: UI Kit --- diff --git a/src/core/client/ui/components/Popover/Popover.mdx b/src/core/client/ui/components/Popover/Popover.mdx index a7a67a04b..6f3dd9ec1 100644 --- a/src/core/client/ui/components/Popover/Popover.mdx +++ b/src/core/client/ui/components/Popover/Popover.mdx @@ -3,24 +3,28 @@ name: Popover menu: UI Kit --- -import { Playground } from 'docz' -import Popover from './Popover' -import Button from '../Button' -import Flex from '../Flex' -import Typography from '../Typography' -import ButtonIcon from '../Button/ButtonIcon' +import { Playground } from "docz"; +import Popover from "./Popover"; +import Button from "../Button"; +import Flex from "../Flex"; +import Typography from "../Typography"; +import ButtonIcon from "../Button/ButtonIcon"; # Popover `Popover` renders a popover dialog attached to another `Element`. ## Basic usage + - This is the body} - > - {({ toggleVisibility, forwardRef }) => ( - )} @@ -28,6 +32,7 @@ import ButtonIcon from '../Button/ButtonIcon' #### Example with `placement=top` + )} > - {({ toggleVisibility, forwardRef }) => ( - )} diff --git a/src/core/client/ui/components/Popover/Popover.tsx b/src/core/client/ui/components/Popover/Popover.tsx index a5b7c58aa..acf6f1494 100644 --- a/src/core/client/ui/components/Popover/Popover.tsx +++ b/src/core/client/ui/components/Popover/Popover.tsx @@ -23,14 +23,16 @@ type Placement = | "left-start"; interface BodyRenderProps { - toggleVisibility: () => void; + /** toggles visibility, if event is provided, the event will stop propagating. */ + toggleVisibility: (event?: React.SyntheticEvent | Event) => void; visible: boolean; scheduleUpdate: () => void; } interface ChildrenRenderProps { - toggleVisibility: () => void; - forwardRef?: RefHandler; + /** toggles visibility, if event is provided, the event will stop propagating. */ + toggleVisibility: (event?: React.SyntheticEvent | Event) => void; + ref?: RefHandler; visible: boolean; } @@ -56,7 +58,10 @@ class Popover extends React.Component { visible: false, }; - public toggleVisibility = () => { + public toggleVisibility = (event?: React.SyntheticEvent | Event) => { + if (event && event.stopPropagation) { + event.stopPropagation(); + } this.setState((state: State) => ({ visible: !state.visible, })); @@ -109,7 +114,7 @@ class Popover extends React.Component { {props => children({ - forwardRef: props.ref, + ref: props.ref, toggleVisibility: this.toggleVisibility, visible: this.state.visible, }) diff --git a/src/core/client/ui/components/index.ts b/src/core/client/ui/components/index.ts index b754a6962..7be2cea72 100644 --- a/src/core/client/ui/components/index.ts +++ b/src/core/client/ui/components/index.ts @@ -48,3 +48,8 @@ export { Marker, Count as MarkerCount } from "./Marker"; export { default as Card } from "./Card"; export { default as PasswordField } from "./PasswordField"; export { TileSelector, Option as TileOption } from "./TileSelector"; +export { + Dropdown, + Divider as DropdownDivider, + Button as DropdownButton, +} from "./Dropdown"; diff --git a/src/core/client/ui/shared/buttonReset.css b/src/core/client/ui/shared/buttonReset.css index 81a4beb2e..2e6ea33ca 100644 --- a/src/core/client/ui/shared/buttonReset.css +++ b/src/core/client/ui/shared/buttonReset.css @@ -21,11 +21,16 @@ background: transparent; font-size: inherit; -webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important; + -webkit-appearance: none; &:disabled { cursor: default; } + &:visited { + color: inherit; + } + &::-moz-focus-inner { border: 0; } diff --git a/src/locales/en-US/admin.ftl b/src/locales/en-US/admin.ftl index 4dcebb6fc..d721b1a44 100644 --- a/src/locales/en-US/admin.ftl +++ b/src/locales/en-US/admin.ftl @@ -9,7 +9,13 @@ navigation-moderate = Moderate navigation-community = Community navigation-stories = Stories navigation-configure = Configure -navigation-signOutButton = Sign Out + +## User Menu +userMenu-signOut = Sign Out +userMenu-viewLatestVersion = View Latest Release +userMenu-reportBug = Report a Bug or Give Feedback +userMenu-popover = + .description = A dialog of the user menu with related links and actions ## Restricted restricted-currentlySignedInTo = Currently signed in to @@ -265,6 +271,8 @@ configure-advanced-permittedDomains-explanation = yourdomain.com, etc. ## Decision History +decisionHistory-popover = + .description = A dialog showing the decision history decisionHistory-youWillSeeAList = You will see a list of your post moderation actions here. decisionHistory-showMoreButton = diff --git a/src/locales/en-US/stream.ftl b/src/locales/en-US/stream.ftl index b69b85ec4..f9085320a 100644 --- a/src/locales/en-US/stream.ftl +++ b/src/locales/en-US/stream.ftl @@ -28,6 +28,8 @@ comments-postCommentForm-submit = Submit comments-stream-loadMore = Load More comments-replyList-showAll = Show All +comments-permalinkPopover = + .description = A dialog showing a permalink to the comment comments-permalinkButton-share = Share comments-permalinkView-viewFullDiscussion = View Full Discussion comments-permalinkView-commentNotFound = Comment not found @@ -104,6 +106,8 @@ profile-profileQuery-storyNotFound = Story not found profile-commentHistory-loadMore = Load More ## Report Comment Popover +comments-reportPopover = + .description = A dialog for reporting comments comments-reportPopover-reportThisComment = Report This Comment comments-reportPopover-whyAreYouReporting = Why are you reporting this comment?