Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Feedback dialog #772

Merged
merged 31 commits into from
Mar 16, 2020
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f4b4f6e
feat: initial commit and experience selector
vpicone Mar 9, 2020
3e7cba1
feat: add data submission
vpicone Mar 9, 2020
fa0a846
feat: add text fadeout
vpicone Mar 9, 2020
2078af4
fix: thicken outline when focused
vpicone Mar 9, 2020
b85ef59
feat: better focus handling
vpicone Mar 9, 2020
02b63c0
fix: focus stealing
vpicone Mar 9, 2020
6ab141c
fix: button text alignment, fieldset spacing, pointer events
vpicone Mar 10, 2020
47df804
fix: focus and style tweaks, use Carbon textbox
vpicone Mar 10, 2020
4371453
feat: add entrance animation
vpicone Mar 11, 2020
62002f2
feat: fade on scroll
vpicone Mar 11, 2020
6efec09
Merge branch 'master' into feedback-dialog
vpicone Mar 11, 2020
34aa206
fix: animation choreography
vpicone Mar 11, 2020
2ba30be
Merge branch 'feedback-dialog' of github.com:vpicone/gatsby-theme-car…
vpicone Mar 11, 2020
68458ef
feat: add livve url
vpicone Mar 12, 2020
950c3ac
fix: safari zamboni and text zoom
vpicone Mar 12, 2020
b86ac5e
fix: safari zamboni and resize
vpicone Mar 12, 2020
7858fe7
fix: animation performance
vpicone Mar 12, 2020
c712134
docs: add Feedback docs
vpicone Mar 12, 2020
230deb5
fix: homepage rendering and console errors
vpicone Mar 12, 2020
10e523a
fix: desktop transition speed
vpicone Mar 12, 2020
e8de24a
fix: focus return and icon flexibility
vpicone Mar 13, 2020
ecdaa7d
cchore: remove unused dep
vpicone Mar 13, 2020
19d0c2a
fix: list overflow weirdness
vpicone Mar 14, 2020
6273273
feat: add submission indicators and mobile styles
vpicone Mar 14, 2020
a408fd7
chore: checkmark tweaks
vpicone Mar 14, 2020
fe18144
fix: launch button active and hover styles
vpicone Mar 14, 2020
44a2c5c
fix: use proper focus token
vpicone Mar 14, 2020
a8f11ab
fix: icon visibility animation
vpicone Mar 14, 2020
d2b7602
fix: slow down stroke, add thanks and adjust reset
vpicone Mar 16, 2020
6c9f847
fix: add background hover change
vpicone Mar 16, 2020
0819436
fix: inverse 01
vpicone Mar 16, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"react/prop-types": 0,
"react/destructuring-assignment": 0,
"react/no-access-state-in-setstate": 0,
"no-useless-escape": 0
"react/jsx-props-no-spreading": 0,
"no-useless-escape": 0,
"arrow-body-style": 0
}
}
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,19 @@
"**/public/"
],
"devDependencies": {
"babel-eslint": "^9.0.0",
"eslint": "^5.16.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-config-prettier": "^4.2.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.8.0",
"eslint-config-airbnb": "^18.0.1",
"eslint-config-prettier": "^6.10.0",
"eslint-config-react-app": "^5.0.2",
"eslint-config-wesbos": "0.0.19",
"eslint-plugin-flowtype": "^4.3.0",
"eslint-plugin-html": "^5.0.3",
"eslint-plugin-html": "^6.0.0",
"eslint-plugin-import": "^2.17.2",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-react": "^7.13.0",
"eslint-plugin-react-hooks": "^1.3.0",
"eslint-plugin-react": "^7.19.0",
"eslint-plugin-react-hooks": "^2.5.0",
"husky": "^3.0.0",
"lerna": "^3.16.2",
"lint-staged": "^9.2.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/example/src/data/nav-items.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
path: /components/DoDontRow
- title: FeatureCard
path: /components/FeatureCard
- title: FeedbackDialog
path: /components/FeedbackDialog
- title: GifPlayer
path: /components/GifPlayer
- title: Grid
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
import ThemeFeedbackDialog from 'gatsby-theme-carbon/src/components/FeedbackDialog/FeedbackDialog';

const FeedbackDialog = ({ props }) => {
const onSubmit = data => {
console.log({ ...data });
};

return <ThemeFeedbackDialog {...props} onSubmit={onSubmit} />;
};

export default FeedbackDialog;
60 changes: 60 additions & 0 deletions packages/example/src/pages/components/FeedbackDialog.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
title: FeatureCard
description: Allow visitors to your website to provide feedback through a dialog
---

<PageDescription>

The `<FeatureDialog>` component is a modeless dialog that allows your users to provide low-friction, anonymous feedback for a specific page.

</PageDescription>

## Activating the dialog

The Feedback button only becomes visisble once you've supplied an `onSubmit` handler. To do that, we'll need to shadow the `FeedbackDialog` component.

1. Create a new javascript file under `src/gatsby-theme-carbon/components/FeedbackDialog/FeedbackDialog.js`. Matching the filepath exactly is important here.

2. Copy the following snippet into your new file

```jsx
import React from 'react';
import ThemeFeedbackDialog from 'gatsby-theme-carbon/src/components/FeedbackDialog/FeedbackDialog';

const FeedbackDialog = ({ props }) => {
const onSubmit = data => {
console.log({ ...data });
};

return <ThemeFeedbackDialog {...props} onSubmit={onSubmit} />;
};

export default FeedbackDialog;

```


## Creating a handler

Next, you'll need a place to send the data. For the Carbon website, we use a serverless function that forwards the data to a [SurveyGizmo](https://www.surveygizmo.com/) quiz.
You can see that function [here](https://github.com/carbon-design-system/carbon-website/blob/master/functions/survey.js).

The handler can send a fetch request off to the endpoint you create. Update the `onSubmit` handler to send the data wherever you want. This function receives the following arguments:
- `experience`: "Negative", "Positive" or "Neutral"
- `comment`: An optional comment
- `path`: the window location when the survey was submitted

```jsx
const FeedbackDialog = ({ props }) => {
const onSubmit = data => {
fetch(process.env.API_ENDPOINT, {
method: 'POST',
body: JSON.stringify(data),
});

return <ThemeFeedbackDialog {...props} onSubmit={onSubmit} />;
};
```



2 changes: 2 additions & 0 deletions packages/gatsby-theme-carbon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@mdx-js/react": "^1.5.5",
"@reach/router": "^1.2.1",
"@vimeo/player": "^2.9.1",
"beautiful-react-hooks": "^0.22.2",
"carbon-components": "^10.10.1",
"carbon-components-react": "^7.10.1",
"classnames": "^2.2.6",
Expand Down Expand Up @@ -58,6 +59,7 @@
"prop-types": "^15.7.2",
"react-helmet": "^6.0.0-beta",
"react-scroll-up": "^1.3.3",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove react-scroll-up now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call

"react-transition-group": "^4.3.0",
"remark-slug": "^5.1.2",
"slugify": "^1.3.4",
"smooth-scroll": "^16.0.3",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import React from 'react';
import { UpToTop20 } from '@carbon/icons-react';
import ScrollToTop from 'react-scroll-up';

import { button } from './BackToTopBtn.module.scss';

const BackToTopBtn = () => (
<ScrollToTop showUnder={300} style={{ zIndex: 9999 }}>
<button className={button} type="button" aria-label="Back to Top">
<UpToTop20 />
</button>
</ScrollToTop>
<button
onClick={() => window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })}
vpicone marked this conversation as resolved.
Show resolved Hide resolved
className={button}
type="button"
aria-label="Back to Top"
>
<UpToTop20 />
</button>
);

export default BackToTopBtn;
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
position: fixed;
bottom: $spacing-09;
right: $spacing-07;
transition: all $transition--base;
transition: all $duration--fast-02;

cursor: pointer;
z-index: 9999;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* eslint-disable jsx-a11y/label-has-associated-control */
// eslint doesn't know about TextArea labeling
import React, { useState } from 'react';
import { TextArea } from 'carbon-components-react';
import cx from 'classnames';
import styles from './Comment.module.scss';

const Comment = () => {
const [focused, setFocused] = useState(false);
const handleBlur = e => {
e.target.scrollTop = 0;
setFocused(false);
};

return (
<div className={styles.container}>
<TextArea
className={styles.textarea}
labelText="Comments (optional):"
onBlur={handleBlur}
onFocus={() => setFocused(true)}
rows={5}
name="feedback-form-comment"
id="feedback-form-comment"
/>
<div
className={cx(styles.fadeout, {
[styles.focused]: focused,
})}
/>
</div>
);
};

export default Comment;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.container {
position: relative;
}

.textarea {
resize: none;
@include carbon--type-style('body-short-02');
@include carbon--breakpoint('md') {
@include carbon--type-style('body-short-01');
}
}

.fadeout {
pointer-events: none;
position: absolute;
bottom: 1px;
left: $spacing-03;
right: $spacing-05;
transition: opacity $duration--fast-02 motion(standard, productive);
opacity: 1;
background: linear-gradient(rgba(57, 57, 57, 0), rgba(57, 57, 57, 1));
height: 3rem;
}

.focused {
opacity: 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, { useState } from 'react';
import {
FaceDissatisfied32,
FaceNeutral32,
FaceSatisfied32,
FaceDissatisfiedFilled32,
FaceNeutralFilled32,
FaceSatisfiedFilled32,
} from '@carbon/icons-react';
import cx from 'classnames';

import styles from './Experience.module.scss';

const NEGATIVE = 'feedback-form-negative';
const NEUTRAL = 'feedback-form-neutral';
const POSITIVE = 'feedback-form-positive';

const Experience = React.forwardRef(function Experience(props, ref) {
const [selected, setSelected] = useState(NEUTRAL);

const onExperienceChange = e => {
setSelected(e.target.id);
};

return (
<fieldset onChange={onExperienceChange}>
<legend>Rate your experience:</legend>
<div className={styles.experienceContainer}>
<label
className={cx(styles.experience, {
[styles.selected]: selected === NEGATIVE,
})}
htmlFor={NEGATIVE}
>
<input
ref={selected === NEGATIVE ? ref : undefined}
type="radio"
id={NEGATIVE}
defaultChecked={selected === NEGATIVE}
name="feedback-form-experience"
value="Negative"
/>
<span>Negative</span>
{selected === NEGATIVE ? (
<FaceDissatisfiedFilled32 />
) : (
<FaceDissatisfied32 />
)}
</label>

<label
className={cx(styles.experience, {
[styles.selected]: selected === NEUTRAL,
})}
htmlFor={NEUTRAL}
>
<input
ref={selected === NEUTRAL ? ref : undefined}
type="radio"
id={NEUTRAL}
defaultChecked={selected === NEUTRAL}
name="feedback-form-experience"
value="Neutral"
/>
<span>Neutral</span>
{selected === NEUTRAL ? <FaceNeutralFilled32 /> : <FaceNeutral32 />}
</label>

<label
className={cx(styles.experience, {
[styles.selected]: selected === POSITIVE,
})}
htmlFor={POSITIVE}
>
<input
ref={selected === POSITIVE ? ref : undefined}
type="radio"
id={POSITIVE}
defaultChecked={selected === POSITIVE}
name="feedback-form-experience"
value="Positive"
/>
<span>Positive</span>
{selected === POSITIVE ? (
<FaceSatisfiedFilled32 />
) : (
<FaceSatisfied32 />
)}
</label>
</div>
</fieldset>
);
});

export default Experience;
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
.experience-container {
display: flex;
background: var(--cds-ui-01);
margin-top: $spacing-03;
}

.experience {
display: flex;
transition: all $duration--fast-02 motion(standard, productive);
justify-content: center;
align-items: center;
position: relative;
height: 96px;
width: 100%;
border-right: 1px solid var(--cds-ui-background);

&:last-of-type {
border-right: none;
}
}

.experience svg {
fill: var(--cds-icon-02);
pointer-events: none;
}

.experience span {
position: absolute;
pointer-events: none;
top: $spacing-03;
left: $spacing-03;
}

.experience input {
position: absolute;
cursor: pointer;
height: 100%;
width: 100%;
appearance: none;
margin: 0;
&:checked {
outline: 1px solid var(--cds-ui-05);
}
&:focus {
outline: 2px solid var(--cds-ui-05);
}
}

.selected svg,
.experience:hover svg {
fill: var(--cds-icon-01);
}

.selected,
.experience:hover {
color: var(--cds-icon-01);
}
Loading