From db2b40543ce4f54924aa2a6cfda8513b61fbf88f Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Thu, 23 Mar 2023 13:26:42 +0100 Subject: [PATCH] docs: remove unleash client from the docs (#3378) We don't use the client for anything at the moment, so we can remove it. While there are things we can use it for in the future, we should instead add it back in when we get there. This also removes the docs feedback form (which we haven't used since the client stopped working) --- website/docusaurus.config.js | 3 - .../UserFeedback/UserFeedback.stories.jsx | 48 -- website/src/components/UserFeedback/index.tsx | 531 ------------------ .../components/UserFeedback/styles.module.css | 244 -------- website/src/theme/Root.tsx | 41 -- 5 files changed, 867 deletions(-) delete mode 100644 website/src/components/UserFeedback/UserFeedback.stories.jsx delete mode 100644 website/src/components/UserFeedback/index.tsx delete mode 100644 website/src/components/UserFeedback/styles.module.css delete mode 100644 website/src/theme/Root.tsx diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index ade265f42e..c53c7f1942 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -53,9 +53,6 @@ module.exports = { markdown: { mermaid: true }, customFields: { // expose env vars etc here - unleashProxyUrl: process.env.UNLEASH_PROXY_URL, - unleashProxyClientKey: process.env.UNLEASH_PROXY_CLIENT_KEY, - unleashFeedbackTargetUrl: process.env.UNLEASH_FEEDBACK_TARGET_URL, environment: process.env.NODE_ENV, }, themeConfig: { diff --git a/website/src/components/UserFeedback/UserFeedback.stories.jsx b/website/src/components/UserFeedback/UserFeedback.stories.jsx deleted file mode 100644 index fc6132fb02..0000000000 --- a/website/src/components/UserFeedback/UserFeedback.stories.jsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; -import { initialData, FeedbackWrapper } from './index'; - -export default { - title: 'User feedback component', - component: FeedbackWrapper, -}; - -const Template = (args) => ; - -export const Step1 = Template.bind({}); -Step1.args = { - open: true, - seedData: { - currentStep: 1, - }, -}; - -export const Step2 = Template.bind({}); -Step2.args = { - seedData: { - currentStep: 2, - }, - open: true, -}; - -export const Step3 = Template.bind({}); -Step3.args = { - seedData: { - currentStep: 3, - }, - open: true, -}; - -export const Step4 = Template.bind({}); -Step4.args = { - seedData: { - currentStep: 4, - }, - open: true, -}; - -export const WithLocalStorage = Template.bind({}); -WithLocalStorage.args = { - open: true, -}; - -export const Closed = Template.bind({}); diff --git a/website/src/components/UserFeedback/index.tsx b/website/src/components/UserFeedback/index.tsx deleted file mode 100644 index cdb86f2ec7..0000000000 --- a/website/src/components/UserFeedback/index.tsx +++ /dev/null @@ -1,531 +0,0 @@ -import React from 'react'; -import styles from './styles.module.css'; -import CloseIcon from '@site/src/icons/close'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; - -const join = (...cs: string[]) => cs.join(' '); - -type CustomerType = 'open source' | 'paying'; - -type FormData = { - score: number; - comment: undefined | string; - customerType: undefined | CustomerType; -}; - -type InitialData = { - currentStep: number; - data: { - score: undefined | number; - comment: undefined | string; - customerType: undefined | CustomerType; - }; - closedOrCompleted: boolean; -}; - -type CompleteData = InitialData & { - initialized: number; -}; - -const clearedData: InitialData = { - currentStep: 1, - data: { - score: undefined, - comment: undefined, - customerType: undefined, - }, - closedOrCompleted: false, -}; - -const localstorageKey = 'user-feedback-v1'; -const populateData = (initialData: InitialData): CompleteData => { - // if we get seed data, use that. Otherwise, check if the last entry in - // localstorage was completed. If not, use that as base. - - const getSeedData = () => { - if (initialData) { - return initialData; - } - - const userFeedbackLog = getUserDataRecord(); - - if (userFeedbackLog) { - const mostRecentTimestamp = Math.max( - ...Object.keys(userFeedbackLog).map(parseInt), - ); - const mostRecent = userFeedbackLog[mostRecentTimestamp]; - if (mostRecent && !mostRecent.closedOrCompleted) { - return mostRecent; - } - } - - return {}; - }; - - const seedData = getSeedData(); - - return { - currentStep: 1, - ...seedData, - data: { - score: undefined, - comment: undefined, - customerType: undefined, - ...seedData?.data, - }, - initialized: Date.now(), - }; -}; - -const getUserDataRecord = () => - JSON.parse(localStorage.getItem(localstorageKey)); - -const storeData = (data: CompleteData) => { - const existingData = getUserDataRecord(); - localStorage.setItem( - localstorageKey, - JSON.stringify({ - ...existingData, - [data.initialized]: data, - }), - ); -}; - -type Message = - | { kind: 'close' } - | { kind: 'completed' } - | { kind: 'reset' } - | { kind: 'set score'; data: number } - | { kind: 'set comment'; data: string } - | { kind: 'set customer type'; data: CustomerType } - | { kind: 'step forward' } - | { kind: 'step back' }; - -const stateReducer = (state: CompleteData, message: Message) => { - switch (message.kind) { - case 'close': - return { ...state, closedOrCompleted: true }; - case 'completed': - return { ...state, closedOrCompleted: true }; - case 'reset': - return { ...populateData(clearedData), closedOrCompleted: false }; - case 'set score': - return { - ...state, - data: { ...state.data, score: message.data }, - }; - case 'set comment': - return { - ...state, - data: { ...state.data, comment: message.data }, - }; - case 'set customer type': - return { - ...state, - data: { ...state.data, customerType: message.data }, - }; - case 'step forward': - return { - ...state, - currentStep: Math.min(state.currentStep + 1, 4), - }; - case 'step back': - return { - ...state, - currentStep: Math.max(state.currentStep - 1, 1), - }; - } -}; - -type Props = { - seedData?: InitialData; - open?: boolean; -}; - -export const FeedbackWrapper: React.FC = ({ seedData, open }) => { - const { - siteConfig: { customFields }, - } = useDocusaurusContext(); - - const feedbackTargetUrl: string | undefined = - (customFields?.unleashFeedbackTargetUrl as string | undefined) ?? - (typeof process !== 'undefined' && - process?.env?.UNLEASH_FEEDBACK_TARGET_URL); - - const [feedbackIsOpen, setFeedbackIsOpen] = React.useState(open); - const [manuallyOpened, setManuallyOpened] = React.useState(open); - - const [state, dispatch] = React.useReducer( - stateReducer, - seedData, - populateData, - ); - - const close = () => dispatch({ kind: 'close' }); - if (feedbackIsOpen) { - storeData(state); - } - - const stepForward = () => { - dispatch({ kind: 'step forward' }); - }; - const stepBack = () => { - dispatch({ kind: 'step back' }); - }; - const setScore = (score: number) => - dispatch({ kind: 'set score', data: score }); - const setComment = (comment: string) => - dispatch({ kind: 'set comment', data: comment }); - const setCustomerType = (customerType: CustomerType) => - dispatch({ kind: 'set customer type', data: customerType }); - - const submitFeedback = (data: FormData) => { - if (feedbackTargetUrl) { - fetch(feedbackTargetUrl, { - method: 'post', - body: JSON.stringify({ - data: { - ...data, - openedManually: manuallyOpened, - currentPage: location.pathname, - }, - }), - headers: { - 'content-type': 'application/json', - }, - }) - .then(async (res) => - res.ok - ? console.log('Success! Feedback was registered.') - : console.warn( - `Oh, no! The feedback registration failed: ${await res.text()}`, - ), - ) - .catch((e) => - console.error( - 'Oh, no! The feedback registration failed:', - e, - ), - ); - } else { - console.warn( - 'No target url specified for feedback. Not doing anything.', - ); - } - dispatch({ kind: 'completed' }); - stepForward(); - }; - - const visuallyHidden = (stepNumber: number) => - state.currentStep !== stepNumber; - const isHidden = (stepNumber: number) => - !feedbackIsOpen || visuallyHidden(stepNumber); - - const Step1 = () => { - const hidden = isHidden(1); - const [newValue, setNewValue] = React.useState(state.data.score); - return ( -
{ - e.preventDefault(); - setScore(newValue); - stepForward(); - }} - aria-hidden={hidden} - > -
-

- - On a scale from 1 to 5 where 1 is very unsatisfied - and 5 is very satisfied, - {' '} - How would you rate your overall satisfaction with the - Unleash documentation? -

- -
- - - {[1, 2, 3, 4, 5].map((n, i) => ( - - { - const value = parseInt( - e.target.value, - ); - setNewValue(value); - }} - autoFocus={ - manuallyOpened - ? state.data.score - ? state.data.score === n - : i === 0 - : false - } - /> - - - ))} - - -
-
- -
-
-
- ); - }; - - const Step2 = () => { - const hidden = isHidden(2); - const textareaId = 'feedback-comment-input'; - const saveComment = () => - setComment( - (document.getElementById(textareaId) as HTMLTextAreaElement) - .value, - ); - - return ( -
{ - e.preventDefault(); - saveComment(); - stepForward(); - }} - > -
- - - -
- - - -
-
-
- ); - }; - - const Step3 = () => { - const hidden = isHidden(3); - const [value, setValue] = React.useState( - state.data.customerType, - ); - - return ( -
{ - e.preventDefault(); - setCustomerType(value); - - // To ensure that we get the correct customer type included. - // We can't rely on the reducer to set it because it won't - // happen until the component re-renders, causing customer - // type to have an old or empty value. - const finalState = stateReducer(state, { - kind: 'set customer type', - data: value, - }); - submitFeedback(finalState.data); - }} - > -
- - Finally, are you a paying customer or an open source - customer of Unleash? - -
- {[ - ['a', 'paying', 'paying'], - ['an', 'open source', 'opensource'], - ].map(([article, customerType, key], i) => ( - - { - setValue(customerType as CustomerType); - }} - /> - - - ))} -
- -
- - -
-
-
- ); - }; - - const Step4 = () => { - const hidden = isHidden(4); - return ( -
-

Thank you! 🙌

- -
- ); - }; - - return ( -
- - -
-
- -
-
- - - - -
-
-
- ); -}; - -export default FeedbackWrapper; diff --git a/website/src/components/UserFeedback/styles.module.css b/website/src/components/UserFeedback/styles.module.css deleted file mode 100644 index 68784bfa54..0000000000 --- a/website/src/components/UserFeedback/styles.module.css +++ /dev/null @@ -1,244 +0,0 @@ -.user-feedback-container { - --outline-style: 2px solid var(--ifm-color-primary); - --row-gap: 1rem; - --element-horizontal-gap: 1rem; - - --animation-duration: 0.25s; - --fade-out-transition: opacity var(--animation-duration); - --fade-in-transition: opacity var(--animation-duration) - calc(var(--animation-duration) / 2); -} - -@media screen and (prefers-reduced-motion: reduced) { - .user-feedback-container { - --animation-duration: 0; - } -} - -.user-feedback { - width: 100%; - position: fixed; - background: var(--ifm-background-color); - bottom: 0; - border: var(--ifm-global-border-width) solid var(--unleash-color-gray); - border-radius: var(--ifm-global-radius) var(--ifm-global-radius) 0 0; - box-shadow: var(--ifm-global-shadow-lw); - padding: var(--ifm-spacing-vertical) var(--ifm-spacing-horizontal); - text-align: center; - transition: var(--fade-in-transition); -} - -.user-feedback fieldset { - border: none; - margin: 0; - padding: 0; - width: 100%; -} - -:is(.user-feedback, .user-feedback fieldset) > * + * { - margin-top: var(--row-gap); -} - -.user-feedback button { - border: none; - border-radius: var(--ifm-global-radius); - padding: var(--ifm-spacing-vertical) calc(var(--ifm-spacing-horizontal) / 2); -} - -.user-feedback form > * + * { - margin-top: var(--row-gap); -} - -.hidden { - display: none; -} - -.user-feedback-container * { - outline-offset: 4px; -} - -.user-feedback-container *:focus-visible { - outline: var(--outline-style); -} - -.satisfaction-input-container { - display: flex; - flex-flow: wrap; - place-content: center; - align-items: center; - gap: var(--element-horizontal-gap); -} - -.satisfaction-input-inputs { - display: flex; - flex-flow: wrap; - place-content: center; - align-items: center; - gap: var(--element-horizontal-gap); -} - -.satisfaction-input-visual-label { - display: none; -} - -@media screen and (min-width: 800px) { - .satisfaction-input-visual-label { - display: inline; - } -} - -@media screen and (max-width: 400px) { - .satisfaction-input-inputs { - gap: calc(var(--element-horizontal-gap) / 2); - } -} - -.user-satisfaction-score-label { - display: grid; - place-content: center; - height: 3em; - width: 3em; - border: var(--ifm-global-border-width) solid currentColor; - border-radius: 50%; -} - -.user-satisfaction-score-input:focus-visible + .user-satisfaction-score-label { - outline: var(--outline-style); -} - -.user-satisfaction-score-label:hover { - color: var(--ifm-color-primary); -} - -.user-satisfaction-score-input:checked + label { - color: var(--ifm-color-primary); - background: var(--ifm-color-primary); - color: var(--ifm-color-primary-contrast-background); - border-color: var(--ifm-color-primary); -} - -.button-container { - margin-top: var(--row-gap); - display: flex; - flex-direction: row-reverse; - justify-content: flex-end; - gap: var(--element-horizontal-gap); -} - -button.close-button { - background: none; - border: none; - border-radius: 50%; - padding: 0; - aspect-ratio: 1; - height: 1em; - color: var(--ifm-font-color-base); -} - -.close-button:hover { - color: var(--ifm-color-primary); -} - -.close-button:active { - color: var(--ifm-color-primary-darker); -} - -.close-button-row { - display: flex; - justify-content: flex-end; -} - -.close-button svg { - fill: currentColor; -} - -.primary-button, -.user-feedback button[type='submit'] { - background-color: var(--ifm-color-primary); - color: var(--ifm-background-color); - padding-inline: calc(var(--ifm-spacing-horizontal) * 4); -} - -.primary-button:hover, -.user-feedback button[type='submit']:hover { - background-color: var(--ifm-color-primary-lighter); -} - -.primary-button:hover, -.user-feedback button[type='submit']:active { - background-color: var(--ifm-color-primary-dark); -} - -.button-secondary { - color: var(--ifm-color-primary); - background: none; -} - -.button-secondary:active { - color: var(--ifm-color-primary-darker); -} - -.button-secondary:hover { - color: var(--ifm-color-primary-lightest); -} - -.user-feedback textarea { - display: block; - width: 100%; - background-color: var(--ifm-background-color); - color: currentColor; - border-radius: var(--ifm-global-radius); - border: var(--ifm-global-border-width) solid var(--ifm-color-emphasis-400); - font-style: normal; - font-family: inherit; - padding: var(--ifm-spacing-vertical) var(--ifm-spacing-horizontal); -} - -.customer-type-inputs { - display: flex; - justify-content: center; - flex-wrap: wrap; - gap: var(--ifm-spacing-horizontal); - accent-color: var(--ifm-color-primary); -} - -.open-feedback-button { - padding-block: var(--ifm-spacing-vertical); - padding-inline: var(--ifm-spacing-horizontal); - border-radius: 0 var(--ifm-global-radius) var(--ifm-global-radius) 0; - border: none; - position: fixed; - bottom: 25vh; - right: 0; - transition: var(--fade-in-transition); - transform: rotate(180deg); - writing-mode: vertical-lr; -} - -/* note: Chrome doesn't support writing-mode on buttons, so we need to add a - span for the text and change the writing-mode there. Simultaneously, Firefox - does some weird stuff with the padding of the text if writing-mode isn't - specified on the button itself, so we need to set that too. */ -.open-feedback-button > span { - writing-mode: vertical-lr; -} - -.invisible, -.open-feedback-button[disabled] { - opacity: 0; - transition: var(--fade-out-transition); - pointer-events: none; -} - -.form-section-container { - display: grid; - align-items: center; - max-width: 850px; - margin: auto; -} - -.form-section-container > * { - grid-column: 1; - grid-row: 1; - transition: var(--fade-in-transition); -} diff --git a/website/src/theme/Root.tsx b/website/src/theme/Root.tsx deleted file mode 100644 index c69cb7cb8d..0000000000 --- a/website/src/theme/Root.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import UserFeedback from '@site/src/components/UserFeedback'; -import { UnleashClient } from 'unleash-proxy-client'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; - -// Default implementation, that you can customize -function Root({ children }) { - const { - siteConfig: { customFields }, - } = useDocusaurusContext(); - - const unleashConfig = { - clientKey: customFields.unleashProxyClientKey as string, - url: customFields.unleashProxyUrl as string, - disableRefresh: true, - appName: `docs.getunleash.io-${customFields.environment}`, - }; - - const [showFeedback, setShowFeedback] = React.useState(false); - - if (typeof fetch !== 'undefined') { - try { - const unleash = new UnleashClient(unleashConfig); - unleash.on('ready', () => { - setShowFeedback(unleash.isEnabled('docs-feedback-survey-v1')); - }); - unleash.start(); - } catch (e) { - console.warn('Unable to initialize the Unleash client:', e.message); - } - } - - return ( - <> - {children} - {showFeedback && } - - ); -} - -export default Root;