diff --git a/package-lock.json b/package-lock.json index c713d638f7..a69da3cca5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3955,6 +3955,130 @@ "vary": "^1.1.2" } }, + "@material/animation": { + "version": "8.0.0-canary.a78ceb112.0", + "resolved": "https://registry.npmjs.org/@material/animation/-/animation-8.0.0-canary.a78ceb112.0.tgz", + "integrity": "sha512-avnYo5iXt64I5nJfNbiYebtZ8sYcgxbje1JpU0mr58VpgDpSRbQCDoJi+/LPDQiNDr/lU3vUM9MEQaECY8KVzw==", + "requires": { + "tslib": "^1.9.3" + } + }, + "@material/base": { + "version": "8.0.0-canary.a78ceb112.0", + "resolved": "https://registry.npmjs.org/@material/base/-/base-8.0.0-canary.a78ceb112.0.tgz", + "integrity": "sha512-gSCgOkjtyrpxGm8BC7mx0dM6yyWda/jSvBcCejlydHQ76fwJ/uV8snBvCdyqgW+E2fDt8Y2o5HyeJSFHAb9q/w==", + "requires": { + "tslib": "^1.9.3" + } + }, + "@material/dom": { + "version": "8.0.0-canary.a78ceb112.0", + "resolved": "https://registry.npmjs.org/@material/dom/-/dom-8.0.0-canary.a78ceb112.0.tgz", + "integrity": "sha512-FmmLO+F/kyo/0rbkkp955JiN8ZCG9CI5g3VH4Ru02jtZdXnGSzo8Cdqjq2fzgP7MQ//eTTwMdeCZfp81UCwjQw==", + "requires": { + "@material/feature-targeting": "8.0.0-canary.a78ceb112.0", + "tslib": "^1.9.3" + } + }, + "@material/feature-targeting": { + "version": "8.0.0-canary.a78ceb112.0", + "resolved": "https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-8.0.0-canary.a78ceb112.0.tgz", + "integrity": "sha512-pYiHlUwKiAwBAULUDVxADD2eHb80cuzwQsQI9IpNr1mgdLcoKomWYxmHYzyD9AUIOlYsPrft+ozyC4gXt5kOtg==" + }, + "@material/mwc-base": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/@material/mwc-base/-/mwc-base-0.17.2.tgz", + "integrity": "sha512-SJ4GLhikChTp7HwyAqccIk1usxwLporj5EWCNx+1h2ECWIY5BdIySFkmq9uqrVLEDTwW0wPB+0ZD+E3ScJdvZw==", + "requires": { + "@material/base": "=8.0.0-canary.a78ceb112.0", + "@material/dom": "=8.0.0-canary.a78ceb112.0", + "lit-element": "^2.3.0", + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + } + } + }, + "@material/mwc-button": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/@material/mwc-button/-/mwc-button-0.17.2.tgz", + "integrity": "sha512-2fNVfF2tL1yywufCY4qLgqBR5lS2QFUIu9AqOmImN0MNmFk3jhXAiVSdWbuMKruiGI0HW4DAzM/w+uiAVu83KA==", + "requires": { + "@material/mwc-icon": "^0.17.2", + "@material/mwc-ripple": "^0.17.2", + "lit-element": "^2.3.0", + "lit-html": "^1.1.2", + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + } + } + }, + "@material/mwc-icon": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/@material/mwc-icon/-/mwc-icon-0.17.2.tgz", + "integrity": "sha512-9EpoLbn82RD5Wsxv0J7o1hTyn+WLfC1tdaTBvDmkRJZhfFheXYKUWNQXWrra0I/BpmojOGvijStOH0rga513WA==", + "requires": { + "lit-element": "^2.3.0", + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + } + } + }, + "@material/mwc-ripple": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/@material/mwc-ripple/-/mwc-ripple-0.17.2.tgz", + "integrity": "sha512-rdx/mR9vxPS6ZnM7oWP4E5FGRQ3pgo7n/hGUPcGMxFfXaxaHs1evoIQscp9a6HcXVrLPXemKD6msObwMDKetQA==", + "requires": { + "@material/dom": "=8.0.0-canary.a78ceb112.0", + "@material/mwc-base": "^0.17.2", + "@material/ripple": "=8.0.0-canary.a78ceb112.0", + "lit-element": "^2.3.0", + "lit-html": "^1.1.2", + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + } + } + }, + "@material/ripple": { + "version": "8.0.0-canary.a78ceb112.0", + "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-8.0.0-canary.a78ceb112.0.tgz", + "integrity": "sha512-0vftc/VRNBGNkVeaumc+ztN6cPUDUXL0+X1HQSuNGn4tS0slqQH6yi9+juZhteOGY9Ca+1D3agsB7H9eNzdM8Q==", + "requires": { + "@material/animation": "8.0.0-canary.a78ceb112.0", + "@material/base": "8.0.0-canary.a78ceb112.0", + "@material/dom": "8.0.0-canary.a78ceb112.0", + "@material/feature-targeting": "8.0.0-canary.a78ceb112.0", + "@material/theme": "8.0.0-canary.a78ceb112.0", + "tslib": "^1.9.3" + } + }, + "@material/theme": { + "version": "8.0.0-canary.a78ceb112.0", + "resolved": "https://registry.npmjs.org/@material/theme/-/theme-8.0.0-canary.a78ceb112.0.tgz", + "integrity": "sha512-/hBvqcXYDuCG0nWEJ8uYsM/XxrCE4EHcel7N6WYwZDRHlPfKQis3qPEJCJicdHckVFdOUvGr9KO2h5BWUNA90Q==", + "requires": { + "@material/feature-targeting": "8.0.0-canary.a78ceb112.0" + } + }, "@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", @@ -22802,8 +22926,7 @@ "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", - "dev": true + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" }, "tsscmp": { "version": "1.0.6", diff --git a/package.json b/package.json index 7a044c8cd0..3f59db0fc1 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ }, "dependencies": { "@google-web-components/google-youtube": "^3.0.1", + "@material/mwc-button": "^0.17.2", "@polymer/app-layout": "^3.1.0", "@polymer/app-route": "^3.0.2", "@polymer/google-map": "github:pranavpandey/google-map#3.0", diff --git a/src/components/auth-required.test.ts b/src/components/auth-required.test.ts new file mode 100644 index 0000000000..e4496a22d8 --- /dev/null +++ b/src/components/auth-required.test.ts @@ -0,0 +1,71 @@ +import { fireEvent } from '@testing-library/dom'; +import { html } from 'lit-html'; +import { mocked } from 'ts-jest/utils'; +import { fixture } from '../../__tests__/helpers/fixtures'; +import { dialogsActions } from '../redux/actions'; +import { SIGN_IN } from '../redux/constants'; +import { store } from '../redux/store'; +import './auth-required'; + +jest.mock('../redux/actions'); + +type AuthRequired = import('./auth-required').AuthRequired; +const openDialog = mocked(dialogsActions.openDialog); + +describe('auth-required', () => { + let element!: AuthRequired; + let shadowRoot!: ShadowRoot; + + beforeAll(async () => { + const render = await fixture( + html` + +

Please sign in

+
Welcome
+
+ ` + ); + + element = render.element; + shadowRoot = render.shadowRoot; + }); + + beforeEach(() => { + openDialog.mockClear(); + }); + + it('should be registered', () => { + expect(customElements.get('auth-required')).toBeDefined(); + }); + + it('shows unauthenticated prompt', () => { + expect(shadowRoot.querySelector('mwc-button')).not.toHaveAttribute('hidden'); + const slots = shadowRoot.querySelectorAll('slot'); + expect(slots).toHaveLength(2); + expect(slots[0]).not.toHaveAttribute('hidden'); + expect(slots[0]).toHaveAttribute('name', 'prompt'); + expect(slots[0].assignedElements()[0]).toHaveTextContent('Please sign in'); + expect(slots[1]).toHaveAttribute('hidden'); + expect(slots[1]).not.toHaveAttribute('name'); + expect(slots[1].assignedElements()[0]).toHaveTextContent('Welcome'); + }); + + it('opens dialog on tap', () => { + fireEvent.click(shadowRoot.querySelector('mwc-button')!); + expect(openDialog).toHaveBeenCalledTimes(1); + expect(openDialog).toHaveBeenCalledWith('signin'); + }); + + it('shows authenticated content', async () => { + store.dispatch({ + type: SIGN_IN, + user: { uid: '1', signedIn: true }, + }); + await element.updateComplete; + expect(shadowRoot.querySelector('mwc-button')).toHaveAttribute('hidden'); + const slots = shadowRoot.querySelectorAll('slot'); + expect(slots).toHaveLength(2); + expect(slots[0]).toHaveAttribute('hidden'); + expect(slots[1]).not.toHaveAttribute('hidden'); + }); +}); diff --git a/src/components/auth-required.ts b/src/components/auth-required.ts new file mode 100644 index 0000000000..bb0103ae50 --- /dev/null +++ b/src/components/auth-required.ts @@ -0,0 +1,41 @@ +import '@material/mwc-button'; +import { customElement, html, internalProperty } from 'lit-element'; +import { ReduxMixin } from '../mixins/redux-mixin'; +import { dialogsActions } from '../redux/actions'; +import { DIALOGS } from '../redux/constants'; +import { ThemedElement } from './themed-element'; + +@customElement('auth-required') +export class AuthRequired extends ReduxMixin(ThemedElement) { + @internalProperty() + private signedIn = false; + + render() { + return html` +
+ + + +
+ `; + } + + stateChanged(state: import('../redux/store').State) { + this.signedIn = state.user.signedIn; + } + + private signIn() { + dialogsActions.openDialog(DIALOGS.SIGNIN); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'auth-required': AuthRequired; + } +} diff --git a/src/elements/auth-required.ts b/src/elements/auth-required.ts deleted file mode 100644 index 13f18f807f..0000000000 --- a/src/elements/auth-required.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { html, PolymerElement } from '@polymer/polymer'; -import { ReduxMixin } from '../mixins/redux-mixin'; -import { dialogsActions } from '../redux/actions'; -import { DIALOGS } from '../redux/constants'; -import './shared-styles'; - -class AuthRequired extends ReduxMixin(PolymerElement) { - static get template() { - return html` - - - {$ signIn $} - - - `; - } - - static get is() { - return 'auth-required'; - } - - private user: { signedIn?: boolean } = {}; - private dialogs = { signin: { isOpened: false } }; - - static get properties() { - return { - user: Object, - dialogs: Object, - }; - } - - stateChanged(state: import('../redux/store').State) { - this.setProperties({ - user: state.user, - dialogs: state.dialogs, - }); - } - - static get observers() { - return ['_authStatusChanged(user.signedIn)']; - } - - signIn() { - dialogsActions.openDialog(DIALOGS.SIGNIN); - } - - _authStatusChanged(_signedIn) { - if (this.dialogs.signin.isOpened) { - dialogsActions.closeDialog(DIALOGS.SIGNIN); - } - } -} - -customElements.define(AuthRequired.is, AuthRequired); diff --git a/src/elements/dialogs/session-details.ts b/src/elements/dialogs/session-details.ts index ab9f6ae5e9..443c48ac6c 100644 --- a/src/elements/dialogs/session-details.ts +++ b/src/elements/dialogs/session-details.ts @@ -7,13 +7,13 @@ import '@polymer/paper-fab'; import { html, PolymerElement } from '@polymer/polymer'; import { mixinBehaviors } from '@polymer/polymer/lib/legacy/class'; import 'plastic-image'; +import '../../components/auth-required'; import { ReduxMixin } from '../../mixins/redux-mixin'; import { SpeakersHoC } from '../../mixins/speakers-hoc'; import { dialogsActions, sessionsActions, toastActions, uiActions } from '../../redux/actions'; import { DIALOGS } from '../../redux/constants'; import { store } from '../../redux/store'; import { getVariableColor } from '../../utils/functions'; -import '../auth-required'; import '../feedback-block'; import '../shared-styles'; import '../text-truncate'; diff --git a/src/styles/theme.ts b/src/styles/theme.ts index e3b23f1d08..3003633d5b 100644 --- a/src/styles/theme.ts +++ b/src/styles/theme.ts @@ -94,6 +94,11 @@ export const theme = css` transition: border-color var(--animation); } + mwc-button { + --mdc-theme-primary: var(--default-primary-color); + --mdc-theme-on-primary: var(--default-background-color); + } + paper-button { padding: 0.7em; border-radius: 2px;