mirror of
https://github.com/wassname/talk.git
synced 2026-09-13 13:10:43 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
871d3f80c1 | ||
|
|
f7d01174d7 | ||
|
|
6966bb8d12 | ||
|
|
40ad2ed0cb | ||
|
|
802f656caa | ||
|
|
dfdea2b9d2 | ||
|
|
9a9c92d360 | ||
|
|
ceadcc7ae1 | ||
|
|
e0db803a21 | ||
|
|
7a099bbf05 | ||
|
|
51112a11c0 | ||
|
|
6e50b28d8b | ||
|
|
1f72cdbfb3 | ||
|
|
87aba459d6 | ||
|
|
9aa4591655 | ||
|
|
87d32680f0 | ||
|
|
25a1dcdf8d | ||
|
|
e90272aa58 | ||
|
|
bbbff98270 | ||
|
|
fe19ffd7e1 | ||
|
|
7f98f13990 | ||
|
|
1a9bd27236 | ||
|
|
536e804d61 | ||
|
|
43d5d56c90 | ||
|
|
cdcc991255 | ||
|
|
b221baff82 | ||
|
|
5b589bfe2b | ||
|
|
4f5c0e285c | ||
|
|
f26ab60398 | ||
|
|
e7f395190d | ||
|
|
ace252eb72 | ||
|
|
933d82e7c9 | ||
|
|
6e64e6a01c | ||
|
|
bb48e1fe33 | ||
|
|
58a91c2e42 | ||
|
|
6a5a18e3a4 | ||
|
|
d43fbe357a | ||
|
|
5c863f1307 | ||
|
|
d4cb9cde9b | ||
|
|
c59c345756 | ||
|
|
f8234e53ed | ||
|
|
7b2cdbad49 | ||
|
|
00e074d49d | ||
|
|
d848fc193e | ||
|
|
2acc761a5d | ||
|
|
0db5bc846d | ||
|
|
e57940c418 | ||
|
|
ccb8f1defa | ||
|
|
878c3bb3c0 | ||
|
|
86c81c7b9f | ||
|
|
302613a185 | ||
|
|
f3ef9ef778 | ||
|
|
db8c9de0e7 | ||
|
|
f2bf026740 | ||
|
|
625c38a758 | ||
|
|
caa4895b97 | ||
|
|
d1ee7d39f3 | ||
|
|
feaf4858df | ||
|
|
ebe7731222 | ||
|
|
8966dad344 | ||
|
|
4179306cd0 | ||
|
|
2c0153047e | ||
|
|
21cd47f6ed | ||
|
|
2f03b788a4 | ||
|
|
c19f4a35ca | ||
|
|
b39af1f7e6 | ||
|
|
a949d7e881 | ||
|
|
eee909f05b | ||
|
|
72e1b249b1 |
@@ -27,4 +27,3 @@ yarn.lock
|
||||
dist
|
||||
*.css.d.ts
|
||||
__generated__
|
||||
README.md.orig
|
||||
|
||||
+32
-9
@@ -1,7 +1,7 @@
|
||||
const typescriptEslintRecommended = require("@typescript-eslint/eslint-plugin/dist/configs/eslint-recommended")
|
||||
.default.overrides[0];
|
||||
const typescriptRecommended = require("@typescript-eslint/eslint-plugin/dist/configs/recommended.json");
|
||||
const typescriptRecommendedTypeChecking = require("@typescript-eslint/eslint-plugin/dist/configs/recommended-requiring-type-checking.json");
|
||||
.overrides[0];
|
||||
const typescriptRecommended = require("@typescript-eslint/eslint-plugin/dist/configs/recommended.js");
|
||||
const typescriptRecommendedTypeChecking = require("@typescript-eslint/eslint-plugin/dist/configs/recommended-requiring-type-checking.js");
|
||||
const typescriptEslintPrettier = require("eslint-config-prettier/@typescript-eslint");
|
||||
const react = require("eslint-plugin-react").configs.recommended;
|
||||
const jsxA11y = require("eslint-plugin-jsx-a11y").configs.recommended;
|
||||
@@ -41,12 +41,21 @@ const typescriptOverrides = {
|
||||
"error",
|
||||
{ default: "array-simple", readonly: "generic" },
|
||||
],
|
||||
"@typescript-eslint/ban-types": "error",
|
||||
"@typescript-eslint/ban-types": [
|
||||
"error",
|
||||
{
|
||||
types: {
|
||||
"{}": false,
|
||||
object: false,
|
||||
extendDefaults: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/camelcase": "off",
|
||||
"@typescript-eslint/consistent-type-assertions": "error",
|
||||
"@typescript-eslint/consistent-type-definitions": "error",
|
||||
"@typescript-eslint/class-name-casing": "error",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/explicit-member-accessibility": [
|
||||
"error",
|
||||
{
|
||||
@@ -56,7 +65,6 @@ const typescriptOverrides = {
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/indent": "off",
|
||||
"@typescript-eslint/interface-name-prefix": "error",
|
||||
"@typescript-eslint/member-delimiter-style": "off",
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
"@typescript-eslint/no-empty-interface": "error",
|
||||
@@ -64,7 +72,6 @@ const typescriptOverrides = {
|
||||
"@typescript-eslint/no-misused-new": "error",
|
||||
"@typescript-eslint/no-namespace": "error",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-parameter-properties": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{ args: "none", ignoreRestSiblings: true },
|
||||
@@ -85,11 +92,20 @@ const typescriptOverrides = {
|
||||
"react/prop-types": "off",
|
||||
"react/no-unescaped-entities": "off",
|
||||
"no-empty-function": "off",
|
||||
// (tessalt) disabled because video elements are only used to display gifs, which have no audio
|
||||
"jsx-a11y/media-has-caption": "off",
|
||||
}
|
||||
),
|
||||
};
|
||||
|
||||
let typescriptTypeCheckingOverrides = {
|
||||
const jestTypeCheckingOverrides = {
|
||||
files: ["test/**/*.ts", "test/**/*.tsx"],
|
||||
rules: {
|
||||
"@typescript-eslint/no-floating-promises": "off",
|
||||
},
|
||||
};
|
||||
|
||||
const typescriptTypeCheckingOverrides = {
|
||||
files: ["*.ts", "*.tsx"],
|
||||
parserOptions: {
|
||||
project: [
|
||||
@@ -116,12 +132,18 @@ let typescriptTypeCheckingOverrides = {
|
||||
},
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/no-misused-promises": "off",
|
||||
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||
"@typescript-eslint/no-unsafe-member-access": "off",
|
||||
"@typescript-eslint/no-unsafe-call": "off",
|
||||
"@typescript-eslint/no-unsafe-return": "off",
|
||||
// 28.11.19: (cvle) Disabled because behavior of regexp.exec seems different than str.match?
|
||||
"@typescript-eslint/prefer-regexp-exec": "off",
|
||||
"@typescript-eslint/require-await": "off",
|
||||
"@typescript-eslint/no-misused-promises": "off",
|
||||
"@typescript-eslint/restrict-template-expressions": "off",
|
||||
"@typescript-eslint/unbound-method": "off", // 10.10.19: (cvle) seems to give false positive.
|
||||
}),
|
||||
overrides: [jestTypeCheckingOverrides],
|
||||
};
|
||||
|
||||
const jestOverrides = {
|
||||
@@ -169,6 +191,7 @@ module.exports = {
|
||||
"eol-last": "off",
|
||||
eqeqeq: "error",
|
||||
"guard-for-in": "error",
|
||||
"jsdoc/check-param-names": "off",
|
||||
"jsdoc/require-jsdoc": "off",
|
||||
"jsdoc/require-returns": "off",
|
||||
"jsdoc/require-param": "off",
|
||||
|
||||
@@ -20,5 +20,4 @@ yarn.lock
|
||||
dist
|
||||
*.css.d.ts
|
||||
__generated__
|
||||
README.md.orig
|
||||
persisted-queries.json
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# .kodiak.toml
|
||||
|
||||
# Kodiak's configuration file should be placed at `.kodiak.toml` (repository
|
||||
# root) or `.github/.kodiak.toml`.
|
||||
# docs: https://kodiakhq.com/docs/config-reference
|
||||
|
||||
# version is the only required setting in a kodiak config.
|
||||
# `1` is the only valid setting for this field.
|
||||
version = 1
|
||||
|
||||
[merge]
|
||||
# Label to enable Kodiak to merge a PR.
|
||||
|
||||
# By default, Kodiak will only act on PRs that have this label. You can disable
|
||||
# this requirement via `merge.require_automerge_label`.
|
||||
automerge_label = "🚀 merge it!" # default: "automerge"
|
||||
|
||||
# Kodiak will not merge a PR with any of these labels.
|
||||
blocking_labels = ["don't merge"] # default: [], options: list of label names (e.g. ["wip"])
|
||||
|
||||
# Choose merge method for Kodiak to use.
|
||||
#
|
||||
# Kodiak will report a configuration error if the selected merge method is
|
||||
# disabled for a repository.
|
||||
#
|
||||
# If you're using the "Require signed commits" GitHub Branch Protection setting
|
||||
# to require commit signatures, _`"merge"` is the only compatible option_. Any
|
||||
# other option will cause Kodiak to raise a configuration error.
|
||||
method = "squash" # default: "merge", options: "merge", "squash", "rebase"
|
||||
|
||||
# Once a PR is merged, delete the branch. This option behaves like the GitHub
|
||||
# repository setting "Automatically delete head branches", which automatically
|
||||
# deletes head branches after pull requests are merged.
|
||||
delete_branch_on_merge = true # default: false
|
||||
|
||||
# Don't wait for in-progress status checks on a PR to finish before updating the
|
||||
# branch.
|
||||
optimistic_updates = false # default: true
|
||||
|
||||
[merge.message]
|
||||
# Strip HTML comments (`<!-- some HTML comment -->`) from merge commit body.
|
||||
# This setting is useful for stripping HTML comments created by PR templates.
|
||||
# This option only applies when `merge.message.body_type = "markdown"`.
|
||||
strip_html_comments = true # default: false
|
||||
Vendored
+1
-1
@@ -54,5 +54,5 @@
|
||||
"search.exclude": {
|
||||
"package-lock.json": true
|
||||
},
|
||||
"debug.node.autoAttach": "off",
|
||||
"debug.node.autoAttach": "off"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
|
||||
# CSS Variables
|
||||
|
||||
Coral defines a set of CSS Variables that you can use to broadly customize the
|
||||
look and feel of your comment stream. For example you can easily redefine the colors
|
||||
or fonts that are being used.
|
||||
|
||||
To change the CSS Variables, use the following example in your custom CSS file:
|
||||
|
||||
```
|
||||
:root {
|
||||
--font-family-primary: 'Verdana';
|
||||
--round-corners: 0px;
|
||||
}
|
||||
```
|
||||
|
||||
The following list contains the CSS Variables that can be customized.
|
||||
|
||||
**Info**: *Before 6.3.0 Coral uses a different set of CSS Variables. If you are still using
|
||||
the old CSS Variables, please upgrade. Currently we have a compatibility layer to bridge the old CSS Variables to the new ones. This mechanism will be removed in the future.*
|
||||
|
||||
<!-- START docs:css-variables -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN npm run docs:css-variables -->
|
||||
### Index
|
||||
- <a href="#variables">Variables</a>
|
||||
- <a href="#palette">palette</a>
|
||||
- <a href="#palette-primary">palette-primary</a>
|
||||
- <a href="#palette-background">palette-background</a>
|
||||
- <a href="#palette-text">palette-text</a>
|
||||
- <a href="#palette-grey">palette-grey</a>
|
||||
- <a href="#palette-error">palette-error</a>
|
||||
- <a href="#palette-success">palette-success</a>
|
||||
- <a href="#palette-warning">palette-warning</a>
|
||||
- <a href="#font-family">font-family</a>
|
||||
- <a href="#font-weight">font-weight</a>
|
||||
- <a href="#font-weight-primary">font-weight-primary</a>
|
||||
- <a href="#font-weight-secondary">font-weight-secondary</a>
|
||||
- <a href="#font-size">font-size</a>
|
||||
- <a href="#shadow">shadow</a>
|
||||
- <a href="#spacing">spacing</a>
|
||||
- <a href="#mini-unit">mini-unit</a>
|
||||
|
||||
|
||||
### Variables
|
||||
|
||||
Use this to remove round corners or make them more round.
|
||||
|
||||
`--round-corners: 3px;`
|
||||
|
||||
### palette
|
||||
|
||||
`--palette-divider: rgba(0, 0, 0, 0.12);`
|
||||
|
||||
#### palette-primary
|
||||
|
||||
Color palette that is used as the primary color.
|
||||
|
||||
`--palette-primary-100: #EBF6FF; /* Before 6.3.0: --palette-primary-lightest */`
|
||||
|
||||
`--palette-primary-200: #B7DCFF; /* Before 6.3.0: --palette-primary-lighter */`
|
||||
|
||||
`--palette-primary-300: #61B3FF; /* Before 6.3.0: --palette-primary-light */`
|
||||
|
||||
`--palette-primary-400: #2897FF; /* Before 6.3.0: --palette-primary-main */`
|
||||
|
||||
`--palette-primary-500: #0070D9; /* Before 6.3.0: --palette-primary-main */`
|
||||
|
||||
`--palette-primary-600: #0062BE; /* Before 6.3.0: --palette-primary-main */`
|
||||
|
||||
`--palette-primary-700: #005AAE; /* Before 6.3.0: --palette-primary-main */`
|
||||
|
||||
`--palette-primary-800: #004688; /* Before 6.3.0: --palette-primary-dark */`
|
||||
|
||||
`--palette-primary-900: #00386D; /* Before 6.3.0: --palette-primary-darkest */`
|
||||
|
||||
#### palette-background
|
||||
|
||||
Color palette that is used for background colors.
|
||||
|
||||
`--palette-background-body: #FFFFFF;`
|
||||
|
||||
`--palette-background-popover: #FFFFFF;`
|
||||
|
||||
`--palette-background-tooltip: #65696B;`
|
||||
|
||||
`--palette-background-input: #FFFFFF;`
|
||||
|
||||
`--palette-background-input-disabled: #EFEFEF;`
|
||||
|
||||
#### palette-text
|
||||
|
||||
Color palette that is used for text.
|
||||
|
||||
`--palette-text-000: #FFFFFF; /* Before 6.3.0: --palette-text-light */`
|
||||
|
||||
`--palette-text-100: #65696B; /* Before 6.3.0: --palette-text-secondary */`
|
||||
|
||||
`--palette-text-500: #353F44; /* Before 6.3.0: --palette-text-primary */`
|
||||
|
||||
`--palette-text-900: #14171A; /* Before 6.3.0: --palette-text-dark */`
|
||||
|
||||
`--palette-text-placeholder: #9FA4A6; /* Before 6.3.0: --palette-grey-lighter */`
|
||||
|
||||
`--palette-text-input-disabled: #9FA4A6; /* Before 6.3.0: --palette-grey-lighter */`
|
||||
|
||||
#### palette-grey
|
||||
|
||||
Color palette that is used for grey shades.
|
||||
|
||||
`--palette-grey-100: #F4F7F7; /* Before 6.3.0: --palette-grey-lightest */`
|
||||
|
||||
`--palette-grey-200: #EAEFF0; /* Before 6.3.0: --palette-grey-lightest */`
|
||||
|
||||
`--palette-grey-300: #CBD1D2; /* Before 6.3.0: --palette-grey-lighter */`
|
||||
|
||||
`--palette-grey-400: #9FA4A6; /* Before 6.3.0: --palette-grey-lighter */`
|
||||
|
||||
`--palette-grey-500: #65696B; /* Before 6.3.0: --palette-grey-main */`
|
||||
|
||||
`--palette-grey-600: #49545C; /* Before 6.3.0: --palette-grey-dark */`
|
||||
|
||||
`--palette-grey-700: #32404D; /* Before 6.3.0: --palette-grey-darkest */`
|
||||
|
||||
`--palette-grey-800: #202E3E; /* Before 6.3.0: --palette-grey-darkest */`
|
||||
|
||||
`--palette-grey-900: #132033; /* Before 6.3.0: --palette-grey-darkest */`
|
||||
|
||||
#### palette-error
|
||||
|
||||
Color palette that is used for indicating something is error red.
|
||||
|
||||
`--palette-error-100: #FCE5D9; /* Before 6.3.0: --palette-error-lightest */`
|
||||
|
||||
`--palette-error-200: #FAC6B4; /* Before 6.3.0: --palette-error-lighter */`
|
||||
|
||||
`--palette-error-300: #F29D8B; /* Before 6.3.0: --palette-error-lighter */`
|
||||
|
||||
`--palette-error-400: #E5766C; /* Before 6.3.0: --palette-error-light */`
|
||||
|
||||
`--palette-error-500: #D53F3F; /* Before 6.3.0: --palette-error-main */`
|
||||
|
||||
`--palette-error-600: #B72E39; /* Before 6.3.0: --palette-error-main */`
|
||||
|
||||
`--palette-error-700: #991F34; /* Before 6.3.0: --palette-error-dark */`
|
||||
|
||||
`--palette-error-800: #7B142E; /* Before 6.3.0: --palette-error-darkest */`
|
||||
|
||||
`--palette-error-900: #660C2B; /* Before 6.3.0: --palette-error-darkest */`
|
||||
|
||||
#### palette-success
|
||||
|
||||
Color palette that is used for indicating something is success green.
|
||||
|
||||
`--palette-success-100: #D8F9D5; /* Before 6.3.0: --palette-success-lightest */`
|
||||
|
||||
`--palette-success-200: #ADF3AD; /* Before 6.3.0: --palette-success-lighter */`
|
||||
|
||||
`--palette-success-300: #7CDB85; /* Before 6.3.0: --palette-success-lighter */`
|
||||
|
||||
`--palette-success-400: #54B767; /* Before 6.3.0: --palette-success-light */`
|
||||
|
||||
`--palette-success-500: #268742; /* Before 6.3.0: --palette-success-main */`
|
||||
|
||||
`--palette-success-600: #1B743D; /* Before 6.3.0: --palette-success-main */`
|
||||
|
||||
`--palette-success-700: #136138; /* Before 6.3.0: --palette-success-dark */`
|
||||
|
||||
`--palette-success-800: #0C4E32; /* Before 6.3.0: --palette-success-darkest */`
|
||||
|
||||
`--palette-success-900: #07402E; /* Before 6.3.0: --palette-success-darkest */`
|
||||
|
||||
#### palette-warning
|
||||
|
||||
Color palette that is used for indicating a warning and is usually yellow.
|
||||
|
||||
`--palette-warning-100: #FFFACC; /* Before 6.3.0: --palette-warning-main */`
|
||||
|
||||
`--palette-warning-500: #FFE91F; /* Before 6.3.0: --palette-warning-main */`
|
||||
|
||||
### font-family
|
||||
|
||||
Different font families currently in use.
|
||||
|
||||
`--font-family-primary: "Open Sans"; /* Before 6.3.0: --font-family-sans-serif */`
|
||||
|
||||
`--font-family-secondary: "Nunito"; /* Before 6.3.0: --font-family-serif */`
|
||||
|
||||
### font-weight
|
||||
|
||||
Different font weights with matching values for the fonts.
|
||||
|
||||
#### font-weight-primary
|
||||
|
||||
`--font-weight-primary-bold: 700; /* Before 6.3.0: --font-weight-bold */`
|
||||
|
||||
`--font-weight-primary-semi-bold: 600; /* Before 6.3.0: --font-weight-medium */`
|
||||
|
||||
`--font-weight-primary-regular: 300; /* Before 6.3.0: --font-weight-light */`
|
||||
|
||||
#### font-weight-secondary
|
||||
|
||||
`--font-weight-secondary-bold: 700; /* Before 6.3.0: --font-weight-bold */`
|
||||
|
||||
`--font-weight-secondary-regular: 300; /* Before 6.3.0: --font-weight-light */`
|
||||
|
||||
### font-size
|
||||
|
||||
`--font-size-1: 0.75rem;`
|
||||
|
||||
`--font-size-2: 0.875rem;`
|
||||
|
||||
`--font-size-3: 1rem;`
|
||||
|
||||
`--font-size-4: 1.125rem;`
|
||||
|
||||
`--font-size-5: 1.25rem;`
|
||||
|
||||
`--font-size-6: 1.5rem;`
|
||||
|
||||
`--font-size-7: 1.75rem;`
|
||||
|
||||
`--font-size-8: 2rem;`
|
||||
|
||||
`--font-size-9: 2.25rem;`
|
||||
|
||||
`--font-size-icon-xl: 2.25rem;`
|
||||
|
||||
`--font-size-icon-lg: 1.5rem;`
|
||||
|
||||
`--font-size-icon-md: 1.125rem;`
|
||||
|
||||
`--font-size-icon-sm: 0.875rem;`
|
||||
|
||||
`--font-size-icon-xs: 0.75rem;`
|
||||
|
||||
### shadow
|
||||
|
||||
Different shadows that are currently used in Coral.
|
||||
|
||||
`--shadow-popover: 1px 0px 4px rgba(0, 0, 0, 0.25); /* Before 6.3.0: --elevation-main */`
|
||||
|
||||
### spacing
|
||||
|
||||
Different spacing units currenty used in Coral.
|
||||
|
||||
`--spacing-1: 4px;`
|
||||
|
||||
`--spacing-2: 8px;`
|
||||
|
||||
`--spacing-3: 12px;`
|
||||
|
||||
`--spacing-4: 16px;`
|
||||
|
||||
`--spacing-5: 24px;`
|
||||
|
||||
`--spacing-6: 32px;`
|
||||
|
||||
`--spacing-7: 44px;`
|
||||
|
||||
`--spacing-8: 60px;`
|
||||
|
||||
`--spacing-9: 84px;`
|
||||
|
||||
### mini-unit
|
||||
|
||||
Grid units for smaller and larger screens.
|
||||
|
||||
`--mini-unit-small: 4;`
|
||||
|
||||
`--mini-unit-large: 8;`
|
||||
|
||||
<!-- END docs:css-variables -->
|
||||
@@ -1,39 +1,52 @@
|
||||
# Coral 
|
||||
<p align="center">
|
||||
<a href="https://coralproject.net" target="_blank"><img width="250" src="https://docs.coralproject.net/coral/images/coralproject_by_voxmedia.svg" alt="Coral by Vox Media" /></a>
|
||||
</p>
|
||||
|
||||
Online comments are broken. Our open-source commenting platform, Coral, rethinks
|
||||
how moderation, comment display, and conversation function, creating the
|
||||
opportunity for safer, smarter discussions around your work.
|
||||
[Read more about Coral here](https://coralproject.net/talk).
|
||||
<p align="center">
|
||||
A better commenting experience from <a href="https://product.voxmedia.com/" target="_blank">Vox Media</a>.
|
||||
</p>
|
||||
|
||||
Built with <3 by the Coral team, a part of [Vox Media](https://product.voxmedia.com/).
|
||||
<p align="center">
|
||||
<a href="https://circleci.com/gh/coralproject/talk" target="_blank"><img src="https://img.shields.io/circleci/build/gh/coralproject/talk?style=flat-square" alt="CircleCI" /></a>
|
||||
<a href="https://hub.docker.com/r/coralproject/talk" target="_blank"><img src="https://img.shields.io/docker/v/coralproject/talk?label=docker%20hub&sort=semver&style=flat-square" alt="Docker Image Version" /></a>
|
||||
<a href="https://hub.docker.com/r/coralproject/talk" target="_blank"><img src="https://img.shields.io/docker/image-size/coralproject/talk?label=docker%20image%20size&sort=semver&style=flat-square" alt="Docker Image Size" /></a>
|
||||
<a href="https://twitter.com/coralproject" target="_blank"><img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/coralproject?style=flat-square"></a>
|
||||
</p>
|
||||
|
||||
Preview Coral easily by running it via a Heroku App:
|
||||
## Description
|
||||
|
||||
[](https://heroku.com/deploy?template=https://github.com/coralproject/talk)
|
||||
Online comments are broken. Our open-source commenting platform,
|
||||
[Coral](https://coralproject.net), rethinks how moderation, comment display, and
|
||||
conversation function, creating the opportunity for safer, smarter discussions
|
||||
around your work.
|
||||
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
## Table of Contents
|
||||
|
||||
- [Documentation](#documentation)
|
||||
- [Pre-Launch Guide](#pre-launch-guide)
|
||||
- [More Resources](#more-resources)
|
||||
- [License](#license)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
We offer hosting and support packages for Coral, as well as exclusive,
|
||||
customer-only features. [Contact us](https://coralproject.net/pricing/) for more
|
||||
information or [sign up for a webinar](https://coralproject.net).
|
||||
|
||||
## Documentation
|
||||
|
||||
You can get started with Coral using our [Documentation](https://docs.coralproject.net/talk/).
|
||||
If you're new to Coral, the [Coral documentation](https://docs.coralproject.net/)
|
||||
is a great place to start running and developing with Coral.
|
||||
|
||||
## Pre-Launch Guide
|
||||
You’ve installed Coral, and you’re preparing to launch it on your site. The real
|
||||
community work starts now, before you go live. You have a unique opportunity
|
||||
pre-launch to set your community up for success. Read our
|
||||
[Community Guides](https://guides.coralproject.net/start-here/) to learn more.
|
||||
|
||||
You’ve installed Coral on your server, and you’re preparing to launch it on your site. The real community work starts now, before you go live. You have a unique opportunity pre-launch to set your community up for success. Read our [Community Guides](https://guides.coralproject.net/start-here/) to learn more.
|
||||
## Support
|
||||
|
||||
## More Resources
|
||||
We can help you set up Coral, migrate your comments from another system,
|
||||
integrate your registration platform, pair with your programmers, and help you
|
||||
with bespoke installs. To learn more, [contact us](https://coralproject.net/pricing/).
|
||||
|
||||
- [Learn more about Coral and sign up for a webinar](https://coralproject.net/)
|
||||
## Contributing
|
||||
|
||||
Coral is a Apache-2.0 licensed open-source project built with <3 by the Coral
|
||||
team, a part of [Vox Media](https://product.voxmedia.com/).
|
||||
|
||||
If you are interested in contributing to Coral, check out our [Contributor's Guide](CONTRIBUTING.md).
|
||||
|
||||
## License
|
||||
|
||||
Coral is released under the [Apache License, v2.0](/LICENSE).
|
||||
Coral is [Apache-2.0 licensed](LICENSE).
|
||||
|
||||
@@ -10,10 +10,6 @@
|
||||
"description": "The shared secret to use to sign JSON Web Tokens (JWT) with the selected signing algorithm.",
|
||||
"generator": "secret"
|
||||
},
|
||||
"CONCURRENCY": {
|
||||
"description": "The number of worker nodes to spawn to handle traffic.",
|
||||
"value": "1"
|
||||
},
|
||||
"LOCALE": {
|
||||
"description": "Specify the default locale to use for all requests without a locale specified",
|
||||
"value": "en-US"
|
||||
|
||||
+3
-3
@@ -42,8 +42,8 @@ exports.onCreateWebpackConfig = ({
|
||||
|
||||
if (stage === "develop") {
|
||||
config.entry.commons.push(
|
||||
// Add our global css variables file.
|
||||
`${srcDir}/core/client/ui/theme/variables.css.ts`
|
||||
// Add our stream css variables file.
|
||||
`${srcDir}/core/client/ui/theme/stream.css.ts`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ exports.onCreateWebpackConfig = ({
|
||||
if (stage === "build-javascript") {
|
||||
config.entry.app = [
|
||||
config.entry.app,
|
||||
`${appDir}/core/client/ui/theme/variables.css.ts`,
|
||||
`${appDir}/core/client/ui/theme/stream.css.ts`,
|
||||
];
|
||||
}
|
||||
*/
|
||||
|
||||
Generated
+13372
-7939
File diff suppressed because it is too large
Load Diff
+79
-58
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@coralproject/talk",
|
||||
"version": "6.2.3",
|
||||
"version": "6.3.1",
|
||||
"author": "The Coral Project",
|
||||
"homepage": "https://coralproject.net/",
|
||||
"sideEffects": [
|
||||
@@ -21,13 +21,15 @@
|
||||
],
|
||||
"description": "A better commenting experience from Vox Media.",
|
||||
"scripts": {
|
||||
"clean": "gulp clean",
|
||||
"build": "NODE_ENV=production npm-run-all generate-persist --parallel build:client build:server",
|
||||
"build:development": "NODE_ENV=development npm-run-all generate --parallel build:client build:server",
|
||||
"build:client": "ts-node --transpile-only ./scripts/build.ts",
|
||||
"build:server": "gulp server",
|
||||
"migration:create": "ts-node --transpile-only ./scripts/migration/create.ts",
|
||||
"docs:events": "ts-node ./scripts/generateEventDocs.ts ./src/core/client/stream/events.ts ./CLIENT_EVENTS.md",
|
||||
"doctoc": "doctoc --maxlevel=3 --title '## Table of Contents' README.md CLIENT_EVENTS.md CONTRIBUTING.md WEBHOOKS.md EXTERNAL_MODERATION_PHASES.md",
|
||||
"docs:css-variables": "ts-node ./scripts/generateCSSVariablesDocs.ts ./src/core/client/ui/theme/streamVariables.ts ./CSS_VARIABLES.md",
|
||||
"doctoc": "doctoc --maxlevel=3 --title '## Table of Contents' CLIENT_EVENTS.md CONTRIBUTING.md WEBHOOKS.md EXTERNAL_MODERATION_PHASES.md",
|
||||
"generate": "npm-run-all generate:css-types generate:schema generate:relay",
|
||||
"generate-persist": "npm-run-all generate:css-types generate:schema generate:relay-persist",
|
||||
"generate:css-types": "tcm src/core/client/",
|
||||
@@ -41,7 +43,7 @@
|
||||
"generate:schema": "node ./scripts/generateSchemaTypes.js",
|
||||
"docz": "docz",
|
||||
"start": "NODE_ENV=production node dist/index.js",
|
||||
"start:development": "NODE_ENV=development CONCURRENCY=${CONCURRENCY:-1} TS_NODE_PROJECT=./src/tsconfig.json ts-node-dev --inspect --transpile-only --no-notify -r tsconfig-paths/register ./src/index.ts",
|
||||
"start:development": "NODE_ENV=development TS_NODE_PROJECT=./src/tsconfig.json ts-node-dev --inspect --transpile-only --no-notify -r tsconfig-paths/register ./src/index.ts",
|
||||
"start:webpackDevServer": "ts-node --transpile-only ./scripts/start.ts",
|
||||
"lint": "npm-run-all --parallel lint:* tscheck:*",
|
||||
"lint:server": "eslint 'src/**/*.{js,ts,tsx}' --ignore-pattern 'src/core/client/**'",
|
||||
@@ -64,14 +66,18 @@
|
||||
"@fluent/dom": "^0.6.0",
|
||||
"@hapi/joi": "^17.1.1",
|
||||
"@metascraper/helpers": "^5.11.6",
|
||||
"@rudderstack/rudder-sdk-node": "0.0.2",
|
||||
"@types/recharts": "^1.8.9",
|
||||
"abort-controller": "^3.0.0",
|
||||
"akismet-api": "^5.0.0",
|
||||
"apollo-server-express": "^2.11.0",
|
||||
"apollo-server-core": "^2.14.4",
|
||||
"apollo-server-express": "^2.14.2",
|
||||
"archiver": "^3.1.1",
|
||||
"basic-auth": "^2.0.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bull": "^3.13.0",
|
||||
"bunyan": "^1.8.12",
|
||||
"bytes": "^3.1.0",
|
||||
"cheerio": "^1.0.0-rc.3",
|
||||
"consolidate": "0.14.0",
|
||||
"content-security-policy-builder": "^2.1.0",
|
||||
@@ -84,6 +90,7 @@
|
||||
"dataloader": "1.4.0",
|
||||
"dompurify": "^2.0.8",
|
||||
"dotenv": "^8.2.0",
|
||||
"emailjs": "^3.2.0",
|
||||
"env-rewrite": "^1.0.2",
|
||||
"express": "^4.17.1",
|
||||
"express-enforces-ssl": "^1.1.0",
|
||||
@@ -94,7 +101,7 @@
|
||||
"graphql-extensions": "^0.11.0",
|
||||
"graphql-fields": "2.0.3",
|
||||
"graphql-import": "^0.7.1",
|
||||
"graphql-playground-html": "^1.6.13",
|
||||
"graphql-playground-html": "1.6.13",
|
||||
"graphql-redis-subscriptions": "^2.2.1",
|
||||
"graphql-subscriptions": "^1.1.0",
|
||||
"graphql-tools": "^4.0.7",
|
||||
@@ -107,7 +114,7 @@
|
||||
"juice": "^6.0.0",
|
||||
"jwks-rsa": "^1.7.0",
|
||||
"linkifyjs": "^2.1.9",
|
||||
"lodash": "^4.17.15",
|
||||
"lodash": "^4.17.19",
|
||||
"long-settimeout": "^1.0.1",
|
||||
"lru-cache": "^5.1.1",
|
||||
"luxon": "^1.22.2",
|
||||
@@ -115,11 +122,10 @@
|
||||
"metascraper-description": "^5.11.6",
|
||||
"metascraper-image": "^5.11.6",
|
||||
"metascraper-title": "^5.11.6",
|
||||
"mongodb": "^3.5.5",
|
||||
"mongodb": "^3.5.9",
|
||||
"mongodb-core": "^3.2.7",
|
||||
"ms": "^2.1.2",
|
||||
"node-fetch": "^2.6.0",
|
||||
"nodemailer": "^6.4.6",
|
||||
"nunjucks": "^3.2.1",
|
||||
"on-finished": "^2.3.0",
|
||||
"passport": "^0.4.1",
|
||||
@@ -132,10 +138,11 @@
|
||||
"prom-client": "^12.0.0",
|
||||
"proxy-agent": "^3.1.1",
|
||||
"querystringify": "^2.1.1",
|
||||
"react-helmet": "^5.2.1",
|
||||
"recharts": "^1.8.5",
|
||||
"source-map-support": "^0.5.16",
|
||||
"stack-utils": "^2.0.1",
|
||||
"striptags": "^3.1.1",
|
||||
"throng": "^4.0.0",
|
||||
"tsscmp": "^1.0.6",
|
||||
"url-regex": "^5.0.0",
|
||||
"uuid": "^7.0.3",
|
||||
@@ -143,13 +150,13 @@
|
||||
"xregexp": "^4.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/preset-env": "^7.9.0",
|
||||
"@babel/preset-react": "^7.9.4",
|
||||
"@babel/preset-typescript": "^7.9.0",
|
||||
"@babel/runtime-corejs3": "^7.9.2",
|
||||
"@babel/core": "^7.10.3",
|
||||
"@babel/preset-env": "^7.10.3",
|
||||
"@babel/preset-react": "^7.10.1",
|
||||
"@babel/preset-typescript": "^7.10.1",
|
||||
"@babel/runtime-corejs3": "^7.10.3",
|
||||
"@coralproject/npm-run-all": "^4.1.5",
|
||||
"@coralproject/rte": "^0.11.1",
|
||||
"@coralproject/rte": "^1.1.1",
|
||||
"@fluent/react": "^0.11.1",
|
||||
"@intervolga/optimize-cssnano-plugin": "^1.0.6",
|
||||
"@types/archiver": "^3.1.0",
|
||||
@@ -157,13 +164,14 @@
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/bull": "^3.12.1",
|
||||
"@types/bunyan": "^1.8.6",
|
||||
"@types/bytes": "^3.1.0",
|
||||
"@types/case-sensitive-paths-webpack-plugin": "^2.1.4",
|
||||
"@types/cheerio": "^0.22.17",
|
||||
"@types/classnames": "^2.2.10",
|
||||
"@types/common-tags": "^1.8.0",
|
||||
"@types/compression-webpack-plugin": "^2.0.1",
|
||||
"@types/consolidate": "0.14.0",
|
||||
"@types/convict": "^4.2.1",
|
||||
"@types/convict": "^5.2.1",
|
||||
"@types/cookie": "^0.3.3",
|
||||
"@types/cookie-parser": "^1.4.2",
|
||||
"@types/cors": "^2.8.6",
|
||||
@@ -183,10 +191,10 @@
|
||||
"@types/html-minifier": "^3.5.3",
|
||||
"@types/html-minifier-terser": "^5.0.0",
|
||||
"@types/html-to-text": "^1.4.31",
|
||||
"@types/html-webpack-plugin": "^3.2.2",
|
||||
"@types/html-webpack-plugin": "^3.2.3",
|
||||
"@types/ioredis": "^4.14.9",
|
||||
"@types/jest": "^25.1.4",
|
||||
"@types/jest-axe": "^3.2.1",
|
||||
"@types/jest": "^26.0.3",
|
||||
"@types/jest-axe": "^3.2.2",
|
||||
"@types/jsdom": "^16.2.0",
|
||||
"@types/jsonwebtoken": "^8.3.8",
|
||||
"@types/linkifyjs": "^2.1.3",
|
||||
@@ -199,7 +207,6 @@
|
||||
"@types/ms": "^0.7.31",
|
||||
"@types/node": "^12.12.34",
|
||||
"@types/node-fetch": "^2.5.5",
|
||||
"@types/nodemailer": "^6.4.0",
|
||||
"@types/nunjucks": "^3.1.3",
|
||||
"@types/on-finished": "^2.3.1",
|
||||
"@types/passport": "^1.0.3",
|
||||
@@ -227,26 +234,25 @@
|
||||
"@types/source-map-support": "^0.5.1",
|
||||
"@types/stack-trace": "0.0.29",
|
||||
"@types/stack-utils": "^1.0.1",
|
||||
"@types/throng": "^4.0.2",
|
||||
"@types/uuid": "^7.0.2",
|
||||
"@types/verror": "^1.10.3",
|
||||
"@types/vinyl": "^2.0.4",
|
||||
"@types/webpack": "^4.41.10",
|
||||
"@types/webpack-assets-manifest": "^3.0.0",
|
||||
"@types/webpack-bundle-analyzer": "^2.13.3",
|
||||
"@types/webpack-dev-server": "^3.10.1",
|
||||
"@types/webpack": "^4.41.17",
|
||||
"@types/webpack-assets-manifest": "^3.0.1",
|
||||
"@types/webpack-bundle-analyzer": "^3.8.0",
|
||||
"@types/webpack-dev-server": "^3.11.0",
|
||||
"@types/ws": "^7.2.3",
|
||||
"@types/xregexp": "^4.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^2.26.0",
|
||||
"@typescript-eslint/eslint-plugin-tslint": "^2.26.0",
|
||||
"@typescript-eslint/parser": "^2.26.0",
|
||||
"@typescript-eslint/eslint-plugin": "^3.4.0",
|
||||
"@typescript-eslint/eslint-plugin-tslint": "^3.4.0",
|
||||
"@typescript-eslint/parser": "^3.4.0",
|
||||
"autoprefixer": "^9.7.5",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"babel-jest": "^25.2.4",
|
||||
"babel-jest": "^26.1.0",
|
||||
"babel-loader": "^8.1.0",
|
||||
"babel-plugin-lodash": "^3.3.4",
|
||||
"babel-plugin-module-resolver": "^4.0.0",
|
||||
"babel-plugin-relay": "^9.0.0",
|
||||
"babel-plugin-relay": "^9.1.0",
|
||||
"babel-plugin-use-lodash-es": "^0.2.0",
|
||||
"bowser": "^2.9.0",
|
||||
"case-sensitive-paths-webpack-plugin": "^2.3.0",
|
||||
@@ -266,13 +272,13 @@
|
||||
"docz": "^v2.3.0-alpha.14",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-adapter-react-16": "^1.15.2",
|
||||
"enzyme-to-json": "^3.4.4",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-prettier": "^6.10.1",
|
||||
"eslint-plugin-jsdoc": "^22.1.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.2.3",
|
||||
"eslint-plugin-prettier": "^3.1.2",
|
||||
"eslint-plugin-react": "^7.19.0",
|
||||
"enzyme-to-json": "^3.5.0",
|
||||
"eslint": "^7.3.1",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-plugin-jsdoc": "^28.5.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.3.1",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"eslint-plugin-react": "^7.20.0",
|
||||
"eventemitter2": "^6.3.1",
|
||||
"farce": "^0.2.8",
|
||||
"final-form": "4.18.6",
|
||||
@@ -282,23 +288,23 @@
|
||||
"fork-ts-checker-webpack-plugin": "^4.1.2",
|
||||
"found": "^0.4.10",
|
||||
"found-relay": "^0.7.0",
|
||||
"graphql-schema-linter": "^0.2.4",
|
||||
"graphql-schema-linter": "^0.4.0",
|
||||
"graphql-schema-typescript": "^1.3.2",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-babel": "^8.0.0",
|
||||
"gulp-cli": "^2.2.0",
|
||||
"gulp-cli": "^2.3.0",
|
||||
"gulp-sourcemaps": "^2.6.5",
|
||||
"gulp-typescript": "^6.0.0-alpha.1",
|
||||
"html-webpack-plugin": "^4.0.4",
|
||||
"husky": "^4.2.3",
|
||||
"intersection-observer": "^0.7.0",
|
||||
"jest": "^25.2.4",
|
||||
"jest": "^26.1.0",
|
||||
"jest-axe": "^3.4.0",
|
||||
"jest-junit": "^10.0.0",
|
||||
"jest-localstorage-mock": "^2.4.0",
|
||||
"jest-mock-console": "^1.0.0",
|
||||
"jest-junit": "^11.0.1",
|
||||
"jest-localstorage-mock": "^2.4.2",
|
||||
"jest-mock-console": "^1.0.1",
|
||||
"keymaster": "^1.6.2",
|
||||
"lint-staged": "^10.1.1",
|
||||
"lint-staged": "^10.2.11",
|
||||
"loader-utils": "^2.0.0",
|
||||
"lodash-es": "^4.17.15",
|
||||
"marked": "^0.8.2",
|
||||
@@ -328,7 +334,6 @@
|
||||
"react-error-overlay": "^6.0.7",
|
||||
"react-final-form": "6.3.0",
|
||||
"react-final-form-arrays": "^3.1.1",
|
||||
"react-helmet": "^5.2.1",
|
||||
"react-popper": "^1.3.7",
|
||||
"react-relay": "^9.0.0",
|
||||
"react-relay-network-modern": "^4.6.1",
|
||||
@@ -339,7 +344,7 @@
|
||||
"recompose": "^0.30.0",
|
||||
"regenerator-runtime": "^0.13.5",
|
||||
"relay-compiler": "^9.0.0",
|
||||
"relay-compiler-language-typescript": "^12.0.0",
|
||||
"relay-compiler-language-typescript": "^12.0.3",
|
||||
"relay-local-schema": "^0.8.0",
|
||||
"relay-runtime": "^9.0.0",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
@@ -356,29 +361,37 @@
|
||||
"terser-webpack-plugin": "^2.3.5",
|
||||
"thread-loader": "^2.1.3",
|
||||
"timekeeper": "^2.2.0",
|
||||
"ts-jest": "^25.3.0",
|
||||
"ts-loader": "^6.2.2",
|
||||
"ts-node": "^8.8.1",
|
||||
"ts-node-dev": "^1.0.0-pre.44",
|
||||
"ts-jest": "26.1.1",
|
||||
"ts-loader": "^7.0.5",
|
||||
"ts-node": "^8.10.2",
|
||||
"ts-node-dev": "1.0.0-pre.44",
|
||||
"tsconfig-paths": "^3.9.0",
|
||||
"tsconfig-paths-webpack-plugin": "^3.2.0",
|
||||
"tslint": "^6.1.0",
|
||||
"typed-css-modules": "^0.6.3",
|
||||
"tslint": "^6.1.2",
|
||||
"typed-css-modules": "^0.6.4",
|
||||
"typeface-manuale": "^1.1.4",
|
||||
"typeface-nunito": "^1.1.3",
|
||||
"typeface-open-sans": "^0.0.75",
|
||||
"typeface-source-sans-pro": "^1.1.5",
|
||||
"typescript": "^3.8.3",
|
||||
"typescript": "^3.9.5",
|
||||
"typescript-snapshots-plugin": "^1.7.0",
|
||||
"wait-for-expect": "^1.3.0",
|
||||
"webpack": "^4.42.1",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-assets-manifest": "^3.1.1",
|
||||
"webpack-bundle-analyzer": "^3.6.1",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack-dev-server": "^3.10.3",
|
||||
"webpack-bundle-analyzer": "^3.8.0",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-dev-server": "^3.11.0",
|
||||
"whatwg-fetch": "^3.0.0"
|
||||
},
|
||||
"dependencies-pins-documentation": {
|
||||
"ts-node-dev@1.0.0-pre.44": [
|
||||
"Newer versions unbearibly slow"
|
||||
],
|
||||
"ts-jest@25.4.0": [
|
||||
"Newer versions require every definition file to be included",
|
||||
"in tsconfig.json in the `files` field. This is cumbersome and",
|
||||
"here is a discussion of it: https://github.com/kulshekhar/ts-jest/issues/1604"
|
||||
],
|
||||
"wait-for-expect@1.x.x": [
|
||||
"Newer versions breaks the use of jest fake timers"
|
||||
],
|
||||
@@ -396,6 +409,11 @@
|
||||
"Newer versions has problems with radio and checkbox fields.",
|
||||
"Update when the following issue is resolved:",
|
||||
"https://github.com/final-form/react-final-form/issues/683"
|
||||
],
|
||||
"graphql-playground-html@1.6.13": [
|
||||
"A template was broken in a release.",
|
||||
"Update when the following issue is resolved:",
|
||||
"https://github.com/prisma-labs/graphql-playground/pull/1238"
|
||||
]
|
||||
},
|
||||
"husky": {
|
||||
@@ -413,7 +431,10 @@
|
||||
"{src/core/client/stream/events.ts,scripts/generateEventDocs.ts,CLIENT_EVENTS.md}": [
|
||||
"npm run docs:events -- --verify"
|
||||
],
|
||||
"{README,CLIENT_EVENTS,CONTRIBUTING,WEBHOOKS,EXTERNAL_MODERATION_PHASES}.md": [
|
||||
"{src/core/client/ui/theme/sharedVariables.ts,src/core/client/ui/theme/streamVariables.ts,scripts/generateCSSVariablesDocs.ts,CSS_VARIABLES.md}": [
|
||||
"npm run docs:css-variables -- --verify"
|
||||
],
|
||||
"{CLIENT_EVENTS,CONTRIBUTING,WEBHOOKS,EXTERNAL_MODERATION_PHASES}.md": [
|
||||
"npm run doctoc"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
import { stripIndent } from "common-tags";
|
||||
import fs from "fs";
|
||||
import { kebabCase, trim } from "lodash";
|
||||
import path from "path";
|
||||
import ts from "typescript";
|
||||
|
||||
import colors from "../src/core/client/ui/theme/colors";
|
||||
|
||||
/** We collect inforamtion from the AST and put it into DocEntry */
|
||||
interface DocEntry {
|
||||
/** Name of property */
|
||||
key: string;
|
||||
/** Documentation if available */
|
||||
docs?: string;
|
||||
/** Value if it is a leaf */
|
||||
value?: string;
|
||||
/** Children if it is a node */
|
||||
children?: DocEntry[];
|
||||
}
|
||||
|
||||
/**
|
||||
* We use this regexp to find a previous block that we
|
||||
* are going to update in the readme file.
|
||||
*/
|
||||
const BLOCK_REGEXP = /<!-- START docs:css-variables -->(.|\n)*<!-- END docs:css-variables -->/gm;
|
||||
|
||||
/** Sort doc entries will sort childrenless entries first */
|
||||
function sortDocEntries(data: DocEntry[]) {
|
||||
data.forEach((d) => {
|
||||
if (d.children) {
|
||||
d.children = sortDocEntries(d.children);
|
||||
}
|
||||
});
|
||||
return data.sort((a, b) => {
|
||||
if (a.children && !b.children) {
|
||||
return 1;
|
||||
}
|
||||
if (b.children && !a.children) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
/** Generate documentation for all classes in a set of .ts files */
|
||||
function gatherEntries(
|
||||
fileName: string,
|
||||
options: ts.CompilerOptions
|
||||
): DocEntry[] {
|
||||
// Build a program using the set of root file names in fileNames
|
||||
const program = ts.createProgram([fileName], options);
|
||||
|
||||
// Get the checker, we will use it to find more about classes
|
||||
const checker = program.getTypeChecker();
|
||||
|
||||
const data: DocEntry[] = [];
|
||||
const currentSourceFile = program.getSourceFile(fileName)!;
|
||||
|
||||
ts.forEachChild(currentSourceFile, visit);
|
||||
|
||||
return sortDocEntries(data);
|
||||
|
||||
/** visit nodes finding css variables */
|
||||
function visit(node: ts.Node) {
|
||||
if (ts.isVariableStatement(node)) {
|
||||
// TODO (cvle) - Currently the variable name is hardcoded. We might want to change that.
|
||||
if (!node.getFullText().includes("streamVariables")) {
|
||||
return;
|
||||
}
|
||||
const firstChild = node.declarationList.declarations[0];
|
||||
if (ts.isVariableDeclaration(firstChild)) {
|
||||
const symbol = checker.getSymbolAtLocation(firstChild.name);
|
||||
if (symbol) {
|
||||
const type = checker.getTypeOfSymbolAtLocation(
|
||||
symbol,
|
||||
symbol.valueDeclaration
|
||||
);
|
||||
type.getProperties().forEach((property) => {
|
||||
serializePropertySymbol(property);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** This will evaluate `symbol` and addthe doc entries */
|
||||
function serializePropertySymbol(symbol: ts.Symbol, parent?: DocEntry) {
|
||||
const entry: DocEntry = { key: symbol.name };
|
||||
const pt = checker.getTypeOfSymbolAtLocation(
|
||||
symbol,
|
||||
symbol.valueDeclaration
|
||||
);
|
||||
if (pt.symbol?.name === "__object") {
|
||||
pt.getProperties().forEach((p2) => {
|
||||
serializePropertySymbol(p2, entry);
|
||||
});
|
||||
} else {
|
||||
entry.value = symbol.valueDeclaration
|
||||
// Last child contains value.
|
||||
.getChildAt(symbol.valueDeclaration.getChildCount() - 1)!
|
||||
.getFullText();
|
||||
}
|
||||
entry.docs = ts.displayPartsToString(
|
||||
symbol.getDocumentationComment(checker)
|
||||
);
|
||||
if (parent) {
|
||||
if (parent.children) {
|
||||
parent.children.push(entry);
|
||||
} else {
|
||||
parent.children = [entry];
|
||||
}
|
||||
} else {
|
||||
data.push(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds any references to `object` by `variableName` and replaces the text
|
||||
* with the actual value.
|
||||
*
|
||||
* E.g. `replaceObjectVariablesInText("colors.teal100", colors, "colors") === "#E2FAF7"`;
|
||||
*/
|
||||
function replaceObjectVariablesInText(
|
||||
text: string,
|
||||
object: any,
|
||||
variableName: string
|
||||
) {
|
||||
let result = text;
|
||||
Object.keys(object).forEach((c: string) => {
|
||||
if (typeof object[c] === "object") {
|
||||
result = replaceObjectVariablesInText(
|
||||
result,
|
||||
object[c],
|
||||
`${variableName}.${c}`
|
||||
);
|
||||
} else {
|
||||
result = result.replace(`${variableName}.${c}`, object[c]);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* transforms value to be used in the documentation.
|
||||
*/
|
||||
function transformValue(value: string) {
|
||||
let compat = "";
|
||||
value = trim(value);
|
||||
// Detect compat value.
|
||||
if (value.startsWith("compat")) {
|
||||
const result = /compat\((.*), *"(.*)"\)/.exec(value);
|
||||
if (!result) {
|
||||
throw new Error("Unrecognized compat format");
|
||||
}
|
||||
value = result[1];
|
||||
compat = result[2];
|
||||
}
|
||||
// If it's a raw string, evaluate it to get rid of the initial quotes.
|
||||
if (value[0] === '"' || value[0] === "'") {
|
||||
// eslint-disable-next-line no-eval
|
||||
value = eval(value);
|
||||
}
|
||||
// Replace all references to colors.
|
||||
value = replaceObjectVariablesInText(value, colors, "colors");
|
||||
if (compat) {
|
||||
// add compat information.
|
||||
return `${value}; /* Before 6.3.0: --${compat} */`;
|
||||
}
|
||||
return `${value};`;
|
||||
}
|
||||
|
||||
function prefixLines(text: string, prefix: string) {
|
||||
return text.split("\n").join(`\n${prefix}`);
|
||||
}
|
||||
|
||||
function entries2Summary(
|
||||
entries: DocEntry[],
|
||||
keyprefix = "",
|
||||
nestprefix = ""
|
||||
): string {
|
||||
let doc = "";
|
||||
entries.forEach((entry) => {
|
||||
if (!entry.children) {
|
||||
return;
|
||||
}
|
||||
const header = kebabCase(keyprefix + entry.key);
|
||||
doc += `${nestprefix}- <a href="#${header}">${header}</a>\n`;
|
||||
if (entry.children) {
|
||||
doc += entries2Summary(
|
||||
entry.children,
|
||||
keyprefix + entry.key + "-",
|
||||
nestprefix + " "
|
||||
);
|
||||
}
|
||||
});
|
||||
return doc;
|
||||
}
|
||||
|
||||
function entries2Doc(entries: DocEntry[], header = "###", prefix = ""): string {
|
||||
let doc = "";
|
||||
entries.forEach((entry) => {
|
||||
if (entry.children) {
|
||||
doc += `\n${header} ${kebabCase(prefix + entry.key)}\n`;
|
||||
if (entry.docs) {
|
||||
doc += `\n${entry.docs}\n`;
|
||||
}
|
||||
doc += entries2Doc(
|
||||
entry.children,
|
||||
header + "#",
|
||||
`${prefix}${entry.key}-`
|
||||
);
|
||||
} else {
|
||||
if (entry.docs) {
|
||||
doc += `\n${entry.docs}\n`;
|
||||
}
|
||||
doc += `\n\`--${kebabCase(prefix + entry.key)}: ${transformValue(
|
||||
entry.value!
|
||||
)}\`\n`;
|
||||
}
|
||||
});
|
||||
return doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append or update previous documention in markdownFile.
|
||||
*
|
||||
* @param markdownFile The markdown file we want to inject the docs too.
|
||||
* @param entries data as returned by gatherEntries.
|
||||
*/
|
||||
function emitDocs(markdownFile: string, entries: DocEntry[], verify = false) {
|
||||
const previousContent = fs.existsSync(markdownFile)
|
||||
? fs.readFileSync(markdownFile).toString()
|
||||
: "";
|
||||
|
||||
const summary = entries2Summary(entries, "", " ");
|
||||
const list = entries2Doc(entries);
|
||||
|
||||
const output = stripIndent`
|
||||
<!-- START docs:css-variables -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN npm run docs:css-variables -->
|
||||
### Index
|
||||
- <a href="#variables">Variables</a>
|
||||
${prefixLines(summary, " ")}
|
||||
|
||||
### Variables
|
||||
${prefixLines(list, " ")}
|
||||
<!-- END docs:css-variables -->
|
||||
`;
|
||||
|
||||
let newContent;
|
||||
// Find previous block.
|
||||
if (BLOCK_REGEXP.test(previousContent)) {
|
||||
newContent = previousContent.replace(BLOCK_REGEXP, output);
|
||||
} else {
|
||||
newContent = previousContent + "\n" + output;
|
||||
}
|
||||
if (previousContent === newContent) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`${markdownFile} is up to date`);
|
||||
return;
|
||||
}
|
||||
if (verify) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
`${markdownFile} is outdated, please run \`npm run docs:css-variables\``
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
fs.writeFileSync(markdownFile, newContent);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Successfully injected documentation into ${markdownFile}`);
|
||||
}
|
||||
|
||||
function main() {
|
||||
if (process.argv.length < 4) {
|
||||
throw new Error("Must provide path to css variables and a markdown file.");
|
||||
}
|
||||
|
||||
const variableFile = process.argv[2];
|
||||
const markdownFile = process.argv[3];
|
||||
|
||||
// Find tsconfig file.
|
||||
const configFile = ts.findConfigFile(variableFile, fs.existsSync);
|
||||
if (!configFile) {
|
||||
throw new Error("tsconfig file not found");
|
||||
}
|
||||
const configText = fs.readFileSync(configFile).toString();
|
||||
const result = ts.parseConfigFileTextToJson(configFile, configText);
|
||||
if (result.error) {
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
// Parse the JSON raw data into actual consumable compiler options.
|
||||
const config = ts.parseJsonConfigFileContent(
|
||||
result.config,
|
||||
ts.sys,
|
||||
path.dirname(configFile)
|
||||
);
|
||||
|
||||
const entries = gatherEntries(variableFile, config.options);
|
||||
emitDocs(markdownFile, entries, process.argv[4] === "--verify");
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env ts-node
|
||||
|
||||
import dotenv from "dotenv";
|
||||
|
||||
// Apply all the configuration provided in the .env file if it isn't already in
|
||||
|
||||
@@ -76,7 +76,7 @@ export default class CommandExecutor implements Executor {
|
||||
});
|
||||
}
|
||||
|
||||
public execute(filePath: string) {
|
||||
public execute(filePath: string): void {
|
||||
this.spawnProcessPotentiallyDebounced();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,14 +99,14 @@ export default class LongRunningExecutor implements Executor {
|
||||
}
|
||||
|
||||
// This is called before exiting.
|
||||
public async onCleanup() {
|
||||
public async onCleanup(): Promise<void> {
|
||||
this.restartDebounced.cancel();
|
||||
if (this.isRunning) {
|
||||
await this.kill();
|
||||
}
|
||||
}
|
||||
|
||||
public execute(filePath: string) {
|
||||
public execute(filePath: string): void {
|
||||
if (this.isRunning) {
|
||||
this.restartDebounced();
|
||||
return;
|
||||
|
||||
@@ -80,7 +80,10 @@ function filterOnly(
|
||||
}) as Config["watchers"];
|
||||
}
|
||||
|
||||
export default async function watch(config: Config, options: Options = {}) {
|
||||
export default async function watch(
|
||||
config: Config,
|
||||
options: Options = {}
|
||||
): Promise<void> {
|
||||
Joi.assert(config, configSchema);
|
||||
const watcher: Watcher = config.backend || new SaneWatcher();
|
||||
const rootDir = config.rootDir || process.cwd();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import convict from "convict";
|
||||
import os from "os";
|
||||
|
||||
import { LOCALES } from "../common/helpers/i18n/locales";
|
||||
|
||||
@@ -67,7 +68,7 @@ const config = convict({
|
||||
maxCores: {
|
||||
doc: "Set maximum of available cores",
|
||||
format: "nat",
|
||||
default: require("os").cpus().length,
|
||||
default: os.cpus().length,
|
||||
env: "WEBPACK_MAX_CORES",
|
||||
arg: "maxCores",
|
||||
},
|
||||
@@ -77,6 +78,7 @@ export type Config = typeof config;
|
||||
|
||||
export const createClientEnv = (c: Config) => ({
|
||||
NODE_ENV: c.get("env"),
|
||||
WEBPACK: "true",
|
||||
});
|
||||
|
||||
// Setup the base configuration.
|
||||
|
||||
@@ -109,10 +109,10 @@ export default function createWebpackConfig(
|
||||
...ifBuild(
|
||||
new MiniCssExtractPlugin({
|
||||
filename: isProduction
|
||||
? "assets/css/[name].[hash].css"
|
||||
? "assets/css/[name].[contenthash].css"
|
||||
: "assets/css/[name].css",
|
||||
chunkFilename: isProduction
|
||||
? "assets/css/[id].[hash].css"
|
||||
? "assets/css/[id].[contenthash].css"
|
||||
: "assets/css/[id].css",
|
||||
}),
|
||||
isProduction &&
|
||||
@@ -217,10 +217,10 @@ export default function createWebpackConfig(
|
||||
// Generated JS file names (with nested folders).
|
||||
// There will be one main bundle, and one file per asynchronous chunk.
|
||||
filename: isProduction
|
||||
? "assets/js/[name].[chunkhash].js"
|
||||
? "assets/js/[name].[contenthash].js"
|
||||
: "assets/js/[name].js",
|
||||
chunkFilename: isProduction
|
||||
? "assets/js/[name].[chunkhash].chunk.js"
|
||||
? "assets/js/[name].[contenthash].chunk.js"
|
||||
: "assets/js/[name].chunk.js",
|
||||
// We inferred the "public path" (such as / or /my-project) from homepage.
|
||||
publicPath,
|
||||
@@ -351,7 +351,7 @@ export default function createWebpackConfig(
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: isProduction
|
||||
? "assets/media/[name].[hash].[ext]"
|
||||
? "assets/media/[name].[contenthash].[ext]"
|
||||
: "assets/media/[name].[ext]",
|
||||
},
|
||||
},
|
||||
@@ -468,7 +468,7 @@ export default function createWebpackConfig(
|
||||
loader: require.resolve("css-loader"),
|
||||
options: {
|
||||
modules: {
|
||||
localIdentName: "[name]-[local]-[hash:base64:5]",
|
||||
localIdentName: "[name]-[local]-[contenthash]",
|
||||
},
|
||||
importLoaders: 1,
|
||||
sourceMap: !disableSourcemaps,
|
||||
@@ -499,7 +499,7 @@ export default function createWebpackConfig(
|
||||
loader: require.resolve("file-loader"),
|
||||
options: {
|
||||
name: isProduction
|
||||
? "assets/media/[name].[hash:8].[ext]"
|
||||
? "assets/media/[name].[contenthash].[ext]"
|
||||
: "assets/media/[name].[ext]",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -20,7 +20,9 @@ export default {
|
||||
appPublicPath: resolveSrc("core/build/publicPath.js"),
|
||||
appLocales: resolveSrc("locales"),
|
||||
appThemeVariables: resolveSrc("core/client/ui/theme/variables.ts"),
|
||||
appThemeVariablesCSS: resolveSrc("core/client/ui/theme/variables.css"),
|
||||
appSassLikeVariables: resolveSrc("core/client/ui/theme/sassLikeVariables.ts"),
|
||||
appThemeStreamCSS: resolveSrc("core/client/ui/theme/stream.css"),
|
||||
appThemeAdminCSS: resolveSrc("core/client/ui/theme/admin.css"),
|
||||
appThemeMixinsCSS: resolveSrc("core/client/ui/theme/mixins.css"),
|
||||
|
||||
appStreamHTML: resolveSrc("core/client/stream/index.html"),
|
||||
|
||||
@@ -3,7 +3,6 @@ require("ts-node/register");
|
||||
const kebabCase = require("lodash/kebabCase");
|
||||
const mapKeys = require("lodash/mapKeys");
|
||||
const mapValues = require("lodash/mapValues");
|
||||
const pickBy = require("lodash/pickBy");
|
||||
const flat = require("flat");
|
||||
const paths = require("./paths").default;
|
||||
const autoprefixer = require("autoprefixer");
|
||||
@@ -17,23 +16,28 @@ const postcssMixins = require("postcss-mixins");
|
||||
const postcssPrependImports = require("postcss-prepend-imports");
|
||||
const postcssAdvancedVariables = require("postcss-advanced-variables");
|
||||
|
||||
delete require.cache[paths.appThemeVariables];
|
||||
const variables = require(paths.appThemeVariables).default;
|
||||
const flatKebabVariables = mapKeys(
|
||||
mapValues(flat(variables, { delimiter: "-" }), (v) => v.toString()),
|
||||
delete require.cache[paths.appSassLikeVariables];
|
||||
const sassLikeVariables = require(paths.appSassLikeVariables).default;
|
||||
|
||||
const kebabs = mapKeys(
|
||||
mapValues(flat(sassLikeVariables, { delimiter: "-" }), (v) => v.toString()),
|
||||
(_, k) => kebabCase(k)
|
||||
);
|
||||
|
||||
// These are sass style variables used in media queries.
|
||||
const mediaQueryVariables = mapValues(
|
||||
pickBy(flatKebabVariables, (v, k) => k.startsWith("breakpoints-")),
|
||||
// Generate sass-style variables to inject into css
|
||||
const postCssVariables = mapValues(kebabs, (value, key) => {
|
||||
// Add unit to breakpoints.
|
||||
// Add 1 to support mobile first approach where we start
|
||||
// with the smallest screen and gradually add styling for the
|
||||
// next bigger screen. This is realized using `min-width` without
|
||||
// ever using `max-width`.
|
||||
(v) => `${Number.parseInt(v, 10) + 1}px`
|
||||
);
|
||||
if (key.toString().startsWith("breakpoints-")) {
|
||||
return `${Number.parseInt(value, 10) + 1}px`;
|
||||
}
|
||||
|
||||
// Default return the raw value
|
||||
return value;
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
// Necessary for external CSS imports to work
|
||||
@@ -52,7 +56,7 @@ module.exports = {
|
||||
// Support nesting.
|
||||
postcssNested(),
|
||||
// Sass style variables to be used in media queries.
|
||||
postcssAdvancedVariables({ variables: mediaQueryVariables }),
|
||||
postcssAdvancedVariables({ variables: postCssVariables }),
|
||||
// Reduce some calc()
|
||||
postcssCalcFunction(),
|
||||
// Provides a modern CSS environment.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
.bar {
|
||||
height: calc(6 * var(--mini-unit));
|
||||
background-color: var(--palette-text-primary);
|
||||
background-color: var(--palette-text-500);
|
||||
}
|
||||
|
||||
.centered {
|
||||
margin: 10% auto;
|
||||
padding: 0 calc(0.5 * var(--mini-unit));
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { Delay, Flex, Spinner } from "coral-ui/components";
|
||||
import { Delay, Flex, Spinner } from "coral-ui/components/v2";
|
||||
|
||||
const Loading: FunctionComponent = () => (
|
||||
<Flex justifyContent="center">
|
||||
|
||||
@@ -10,7 +10,7 @@ import { initLocalState } from "./local";
|
||||
import localesData from "./locales";
|
||||
|
||||
// Import css variables.
|
||||
import "coral-ui/theme/variables.css";
|
||||
import "coral-ui/theme/stream.css";
|
||||
|
||||
async function main() {
|
||||
await injectConditionalPolyfills();
|
||||
@@ -30,4 +30,4 @@ async function main() {
|
||||
ReactDOM.render(<Index />, document.getElementById("app"));
|
||||
}
|
||||
|
||||
main();
|
||||
void main();
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
.content {
|
||||
font-family: var(--font-family-secondary);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-secondary-bold);
|
||||
font-size: var(--font-size-6);
|
||||
line-height: 1.17;
|
||||
|
||||
color: var(--palette-text-500);
|
||||
|
||||
text-align: center;
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { HorizontalGutter, Typography } from "coral-ui/components";
|
||||
import { HorizontalGutter } from "coral-ui/components/v2";
|
||||
|
||||
import styles from "./NotFound.css";
|
||||
|
||||
const NotFound: FunctionComponent = () => (
|
||||
<HorizontalGutter container="main">
|
||||
<Typography variant="heading3" container="h1">
|
||||
Not Found
|
||||
</Typography>
|
||||
<Localized id="notFound">
|
||||
<div className={styles.content}>Not Found</div>
|
||||
</Localized>
|
||||
</HorizontalGutter>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
.title {
|
||||
font-family: var(--font-family-serif);
|
||||
font-weight: 600;
|
||||
font-family: var(--font-family-secondary);
|
||||
font-style: normal;
|
||||
font-size: calc(48rem / var(--rem-base));
|
||||
line-height: calc(42em / 48);
|
||||
text-align: center;
|
||||
font-weight: var(--font-weight-secondary-bold);
|
||||
font-size: var(--font-size-6);
|
||||
line-height: 1.17;
|
||||
|
||||
color: var(--palette-common-black);
|
||||
color: var(--palette-text-500);
|
||||
|
||||
padding-left: var(--spacing-2);
|
||||
}
|
||||
|
||||
.content {
|
||||
@@ -18,14 +19,15 @@
|
||||
}
|
||||
|
||||
.sectionText {
|
||||
font-family: var(--font-family-sans-serif);
|
||||
font-weight: normal;
|
||||
font-family: var(--font-family-primary);
|
||||
font-style: normal;
|
||||
font-size: calc(24rem / var(--rem-base));
|
||||
line-height: calc(34em / 24);
|
||||
letter-spacing: 0.3px;
|
||||
font-weight: var(--font-weight-primary-regular);
|
||||
font-size: var(--font-size-3);
|
||||
line-height: 1.25;
|
||||
|
||||
color: var(--palette-text-primary);
|
||||
color: var(--palette-text-500);
|
||||
|
||||
margin-bottom: var(--spacing-3);
|
||||
}
|
||||
|
||||
.list {
|
||||
@@ -34,7 +36,7 @@
|
||||
}
|
||||
|
||||
.list li {
|
||||
margin-bottom: calc(16em / 24);
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.listContent {
|
||||
@@ -43,6 +45,6 @@
|
||||
|
||||
.bullet {
|
||||
display: inline-block;
|
||||
min-width: 24px;
|
||||
min-width: 16px;
|
||||
margin-right: var(--spacing-2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Localized } from "@fluent/react/compat";
|
||||
import cn from "classnames";
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { Flex, HorizontalGutter, Icon, Typography } from "coral-ui/components";
|
||||
import { Flex, HorizontalGutter, Icon } from "coral-ui/components/v2";
|
||||
|
||||
import styles from "./DownloadDescription.css";
|
||||
|
||||
@@ -10,84 +10,72 @@ const DownloadDescription: FunctionComponent = () => {
|
||||
return (
|
||||
<HorizontalGutter size="double">
|
||||
<Localized id="download-landingPage-title">
|
||||
<h1 className={styles.title}>Download Your Comment History</h1>
|
||||
<h1 className={styles.title}>Download your comment history</h1>
|
||||
</Localized>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.section}>
|
||||
<Localized id="download-landingPage-description">
|
||||
<Typography variant="bodyCopy" className={styles.sectionText}>
|
||||
<div className={styles.sectionText}>
|
||||
Your comment history will be downloaded into a .zip file. After
|
||||
your comment history is unzipped you will have a comma separated
|
||||
value (or .csv) file that you can easily import into your favorite
|
||||
spreadsheet application.
|
||||
</Typography>
|
||||
</div>
|
||||
</Localized>
|
||||
</div>
|
||||
|
||||
<div className={styles.section}>
|
||||
<Localized id="download-landingPage-contentsDescription">
|
||||
<Typography variant="bodyCopy" className={styles.sectionText}>
|
||||
<div className={styles.sectionText}>
|
||||
For each of your comments the following information is included:
|
||||
</Typography>
|
||||
</div>
|
||||
</Localized>
|
||||
<ul className={styles.list}>
|
||||
<li>
|
||||
<Flex alignItems="center">
|
||||
<Icon size="lg" className={styles.bullet}>
|
||||
<Flex alignItems="flex-start">
|
||||
<Icon size="md" className={styles.bullet}>
|
||||
check
|
||||
</Icon>
|
||||
<Localized id="download-landingPage-contentsDate">
|
||||
<Typography
|
||||
variant="bodyCopy"
|
||||
className={cn(styles.sectionText, styles.listContent)}
|
||||
>
|
||||
<div className={cn(styles.sectionText, styles.listContent)}>
|
||||
When you wrote the comment
|
||||
</Typography>
|
||||
</div>
|
||||
</Localized>
|
||||
</Flex>
|
||||
</li>
|
||||
<li>
|
||||
<Flex alignItems="center">
|
||||
<Icon size="lg" className={styles.bullet}>
|
||||
<Flex alignItems="flex-start">
|
||||
<Icon size="md" className={styles.bullet}>
|
||||
check
|
||||
</Icon>
|
||||
<Localized id="download-landingPage-contentsUrl">
|
||||
<Typography
|
||||
variant="bodyCopy"
|
||||
className={cn(styles.sectionText, styles.listContent)}
|
||||
>
|
||||
<div className={cn(styles.sectionText, styles.listContent)}>
|
||||
The permalink URL for the comment
|
||||
</Typography>
|
||||
</div>
|
||||
</Localized>
|
||||
</Flex>
|
||||
</li>
|
||||
<li>
|
||||
<Flex alignItems="center">
|
||||
<Icon size="lg" className={styles.bullet}>
|
||||
<Flex alignItems="flex-start">
|
||||
<Icon size="md" className={styles.bullet}>
|
||||
check
|
||||
</Icon>
|
||||
<Localized id="download-landingPage-contentsText">
|
||||
<Typography
|
||||
variant="bodyCopy"
|
||||
className={cn(styles.sectionText, styles.listContent)}
|
||||
>
|
||||
<div className={cn(styles.sectionText, styles.listContent)}>
|
||||
The comment text
|
||||
</Typography>
|
||||
</div>
|
||||
</Localized>
|
||||
</Flex>
|
||||
</li>
|
||||
<li>
|
||||
<Flex alignItems="center">
|
||||
<Icon size="lg" className={styles.bullet}>
|
||||
<Flex alignItems="flex-start">
|
||||
<Icon size="md" className={styles.bullet}>
|
||||
check
|
||||
</Icon>
|
||||
<Localized id="download-landingPage-contentsStoryUrl">
|
||||
<Typography
|
||||
variant="bodyCopy"
|
||||
className={cn(styles.sectionText, styles.listContent)}
|
||||
>
|
||||
<div className={cn(styles.sectionText, styles.listContent)}>
|
||||
The URL on the article or story where the comment appears
|
||||
</Typography>
|
||||
</div>
|
||||
</Localized>
|
||||
</Flex>
|
||||
</li>
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
.form {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.downloadButton {
|
||||
font-size: calc(18rem / var(--rem-base));
|
||||
line-height: calc(23em / 18);
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import React, { FunctionComponent, useCallback, useState } from "react";
|
||||
|
||||
import { Button, HorizontalGutter } from "coral-ui/components";
|
||||
import { HorizontalGutter } from "coral-ui/components/v2";
|
||||
import { Button } from "coral-ui/components/v3";
|
||||
|
||||
import styles from "./DownloadForm.css";
|
||||
|
||||
@@ -25,15 +26,16 @@ const DownloadForm: FunctionComponent<Props> = ({ token }) => {
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<input name="token" type="hidden" value={token} />
|
||||
<Localized id="download-landingPage-downloadComments">
|
||||
<Localized id="download-landingPage-download">
|
||||
<Button
|
||||
type="submit"
|
||||
variant="filled"
|
||||
color="primary"
|
||||
paddingSize="medium"
|
||||
disabled={submitted}
|
||||
className={styles.downloadButton}
|
||||
upperCase
|
||||
>
|
||||
Download My Comment History
|
||||
Download
|
||||
</Button>
|
||||
</Localized>
|
||||
</form>
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
.root {
|
||||
display: inline-block;
|
||||
|
||||
max-width: calc(70 * var(--mini-unit));
|
||||
max-width: calc(70 * var(--spacing-2));
|
||||
|
||||
@media (min-width: $breakpoints-xs) {
|
||||
max-width: calc(90 * var(--mini-unit));
|
||||
max-width: calc(60 * var(--spacing-2));
|
||||
}
|
||||
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useToken } from "coral-framework/hooks";
|
||||
import { createFetch } from "coral-framework/lib/relay";
|
||||
import { withRouteConfig } from "coral-framework/lib/router";
|
||||
import { parseHashQuery } from "coral-framework/utils";
|
||||
import { HorizontalGutter } from "coral-ui/components";
|
||||
import { HorizontalGutter } from "coral-ui/components/v2";
|
||||
|
||||
import DownloadDescription from "./DownloadDescription";
|
||||
import DownloadForm from "./DownloadForm";
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
.icon {
|
||||
margin-right: var(--spacing-2);
|
||||
}
|
||||
|
||||
.callout {
|
||||
justify-content: left;
|
||||
}
|
||||
@@ -1,22 +1,21 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { CallOut, Flex, Icon } from "coral-ui/components";
|
||||
|
||||
import styles from "./Sorry.css";
|
||||
import { Icon } from "coral-ui/components/v2";
|
||||
import { CallOut } from "coral-ui/components/v3";
|
||||
|
||||
const Sorry: FunctionComponent = () => {
|
||||
return (
|
||||
<CallOut color="error" fullWidth className={styles.callout}>
|
||||
<Flex>
|
||||
<Icon size="md" className={styles.icon}>
|
||||
error
|
||||
</Icon>
|
||||
<CallOut
|
||||
color="error"
|
||||
icon={<Icon>error</Icon>}
|
||||
titleWeight="semiBold"
|
||||
title={
|
||||
<Localized id="download-landingPage-sorry">
|
||||
Your download link is invalid.
|
||||
</Localized>
|
||||
</Flex>
|
||||
</CallOut>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
.title {
|
||||
font-family: var(--font-family-secondary);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-secondary-bold);
|
||||
font-size: var(--font-size-6);
|
||||
line-height: 1.17;
|
||||
|
||||
color: var(--palette-text-500);
|
||||
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
.description {
|
||||
font-family: var(--font-family-primary);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-primary-regular);
|
||||
font-size: var(--font-size-3);
|
||||
line-height: 1.25;
|
||||
|
||||
color: var(--palette-text-500);
|
||||
|
||||
margin-bottom: var(--spacing-3);
|
||||
}
|
||||
|
||||
.label {
|
||||
font-family: var(--font-family-primary);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-primary-bold);
|
||||
font-size: var(--font-size-2);
|
||||
line-height: 1.14;
|
||||
|
||||
color: var(--palette-text-500);
|
||||
|
||||
margin-bottom: var(--spacing-1);
|
||||
}
|
||||
|
||||
.labelDescription {
|
||||
font-family: var(--font-family-primary);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-primary-regular);
|
||||
font-size: var(--font-size-2);
|
||||
line-height: 1.285;
|
||||
|
||||
color: var(--palette-text-100);
|
||||
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
.submit {
|
||||
margin-top: var(--spacing-5);
|
||||
margin-bottom: var(--spacing-1);
|
||||
}
|
||||
@@ -5,10 +5,12 @@ import { Form } from "react-final-form";
|
||||
|
||||
import { InvalidRequestError } from "coral-framework/lib/errors";
|
||||
import { useMutation } from "coral-framework/lib/relay";
|
||||
import { Button, HorizontalGutter, Typography } from "coral-ui/components";
|
||||
import { Button } from "coral-ui/components/v3";
|
||||
|
||||
import ConfirmMutation from "./ConfirmMutation";
|
||||
|
||||
import styles from "./Confirm.css";
|
||||
|
||||
interface Props {
|
||||
token: string;
|
||||
disabled?: boolean;
|
||||
@@ -33,18 +35,18 @@ const ConfirmForm: React.FunctionComponent<Props> = ({ onSuccess, token }) => {
|
||||
<Form onSubmit={onSubmit}>
|
||||
{({ handleSubmit, submitting }) => (
|
||||
<form autoComplete="off" onSubmit={handleSubmit}>
|
||||
<HorizontalGutter size="double">
|
||||
<HorizontalGutter>
|
||||
<Localized id="confirmEmail-emailConfirmation">
|
||||
<Typography variant="heading1">Email Confirmation</Typography>
|
||||
<div>
|
||||
<div>
|
||||
<Localized id="confirmEmail-confirmYourEmailAddress">
|
||||
<div className={styles.title}>Confirm your email address</div>
|
||||
</Localized>
|
||||
<Localized id="confirmEmail-pleaseClickToConfirm">
|
||||
<Typography variant="bodyCopy">
|
||||
<div className={styles.description}>
|
||||
Click below to confirm your email address.
|
||||
</Typography>
|
||||
</div>
|
||||
</Localized>
|
||||
</HorizontalGutter>
|
||||
<HorizontalGutter>
|
||||
</div>
|
||||
<div>
|
||||
<Localized id="confirmEmail-confirmEmail">
|
||||
<Button
|
||||
type="submit"
|
||||
@@ -52,12 +54,14 @@ const ConfirmForm: React.FunctionComponent<Props> = ({ onSuccess, token }) => {
|
||||
color="primary"
|
||||
disabled={submitting}
|
||||
fullWidth
|
||||
upperCase
|
||||
className={styles.submit}
|
||||
>
|
||||
Confirm email
|
||||
</Button>
|
||||
</Localized>
|
||||
</HorizontalGutter>
|
||||
</HorizontalGutter>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</Form>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import React from "react";
|
||||
|
||||
import { CallOut, HorizontalGutter, Typography } from "coral-ui/components";
|
||||
import { CallOut } from "coral-ui/components/v3";
|
||||
|
||||
import styles from "./Confirm.css";
|
||||
|
||||
interface Props {
|
||||
reason: React.ReactNode;
|
||||
@@ -9,23 +11,27 @@ interface Props {
|
||||
|
||||
const Sorry: React.FunctionComponent<Props> = ({ reason }) => {
|
||||
return (
|
||||
<HorizontalGutter size="double">
|
||||
<div>
|
||||
<Localized id="confirmEmail-oopsSorry">
|
||||
<Typography variant="heading1">Oops Sorry!</Typography>
|
||||
<div className={styles.title}>Oops Sorry!</div>
|
||||
</Localized>
|
||||
<CallOut color="error" fullWidth>
|
||||
{reason ? (
|
||||
reason
|
||||
) : (
|
||||
<Localized id="account-tokenNotFound">
|
||||
<span data-testid="invalid-link">
|
||||
The specified link is invalid, check to see if it was copied
|
||||
correctly.
|
||||
</span>
|
||||
</Localized>
|
||||
)}
|
||||
</CallOut>
|
||||
</HorizontalGutter>
|
||||
<CallOut
|
||||
color="error"
|
||||
titleWeight="semiBold"
|
||||
title={
|
||||
reason ? (
|
||||
reason
|
||||
) : (
|
||||
<Localized id="account-tokenNotFound">
|
||||
<span data-testid="invalid-link">
|
||||
The specified link is invalid, check to see if it was copied
|
||||
correctly.
|
||||
</span>
|
||||
</Localized>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import React from "react";
|
||||
|
||||
import { HorizontalGutter, Typography } from "coral-ui/components";
|
||||
import styles from "./Confirm.css";
|
||||
|
||||
const Success: React.FunctionComponent = () => {
|
||||
return (
|
||||
<HorizontalGutter size="double">
|
||||
<div>
|
||||
<Localized id="confirmEmail-successfullyConfirmed">
|
||||
<Typography variant="heading1">Email successfully confirmed</Typography>
|
||||
<div className={styles.title}>Email successfully confirmed</div>
|
||||
</Localized>
|
||||
<Localized id="confirmEmail-youMayClose">
|
||||
<Typography variant="bodyCopy">
|
||||
You may now close this window.
|
||||
</Typography>
|
||||
<div className={styles.description}>You may now close this window.</div>
|
||||
</Localized>
|
||||
</HorizontalGutter>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import React from "react";
|
||||
|
||||
import { CallOut, HorizontalGutter, Typography } from "coral-ui/components";
|
||||
import { CallOut } from "coral-ui/components/v3";
|
||||
|
||||
import styles from "./Unsubscribe.css";
|
||||
|
||||
interface Props {
|
||||
reason: React.ReactNode;
|
||||
@@ -9,23 +11,27 @@ interface Props {
|
||||
|
||||
const Sorry: React.FunctionComponent<Props> = ({ reason }) => {
|
||||
return (
|
||||
<HorizontalGutter size="double">
|
||||
<div>
|
||||
<Localized id="unsubscribe-oopsSorry">
|
||||
<Typography variant="heading1">Oops Sorry!</Typography>
|
||||
<div className={styles.title}>Oops Sorry!</div>
|
||||
</Localized>
|
||||
<CallOut color="error" fullWidth>
|
||||
{reason ? (
|
||||
reason
|
||||
) : (
|
||||
<Localized id="account-tokenNotFound">
|
||||
<span data-testid="invalid-link">
|
||||
The specified link is invalid, check to see if it was copied
|
||||
correctly.
|
||||
</span>
|
||||
</Localized>
|
||||
)}
|
||||
</CallOut>
|
||||
</HorizontalGutter>
|
||||
<CallOut
|
||||
color="error"
|
||||
titleWeight="semiBold"
|
||||
title={
|
||||
reason ? (
|
||||
reason
|
||||
) : (
|
||||
<Localized id="account-tokenNotFound">
|
||||
<span data-testid="invalid-link">
|
||||
The specified link is invalid, check to see if it was copied
|
||||
correctly.
|
||||
</span>
|
||||
</Localized>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import React from "react";
|
||||
|
||||
import { HorizontalGutter, Typography } from "coral-ui/components";
|
||||
import styles from "./Unsubscribe.css";
|
||||
|
||||
const Success: React.FunctionComponent = () => {
|
||||
return (
|
||||
<HorizontalGutter data-testid="success" size="double">
|
||||
<Localized id="unsubscribe-successfullyUnsubscribed">
|
||||
<Typography variant="heading1">
|
||||
You are now unsubscribed from all notifications
|
||||
</Typography>
|
||||
<div data-testid="success">
|
||||
<Localized id="unsubscribe-unsubscribedSuccessfully">
|
||||
<div className={styles.title}>
|
||||
Unsubscribed successfully from email notifications
|
||||
</div>
|
||||
</Localized>
|
||||
</HorizontalGutter>
|
||||
<Localized id="unsubscribe-youMayNowClose">
|
||||
<div className={styles.description}>You may now close this window</div>
|
||||
</Localized>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
.title {
|
||||
font-family: var(--font-family-secondary);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-secondary-bold);
|
||||
font-size: var(--font-size-6);
|
||||
line-height: 1.17;
|
||||
|
||||
color: var(--palette-text-500);
|
||||
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
.description {
|
||||
font-family: var(--font-family-primary);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-primary-regular);
|
||||
font-size: var(--font-size-3);
|
||||
line-height: 1.25;
|
||||
|
||||
color: var(--palette-text-500);
|
||||
|
||||
margin-bottom: var(--spacing-3);
|
||||
}
|
||||
|
||||
.label {
|
||||
font-family: var(--font-family-primary);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-primary-bold);
|
||||
font-size: var(--font-size-2);
|
||||
line-height: 1.14;
|
||||
|
||||
color: var(--palette-text-500);
|
||||
|
||||
margin-bottom: var(--spacing-1);
|
||||
}
|
||||
|
||||
.labelDescription {
|
||||
font-family: var(--font-family-primary);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-primary-regular);
|
||||
font-size: var(--font-size-2);
|
||||
line-height: 1.285;
|
||||
|
||||
color: var(--palette-text-100);
|
||||
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
.submit {
|
||||
margin-top: var(--spacing-5);
|
||||
margin-bottom: var(--spacing-1);
|
||||
}
|
||||
@@ -5,15 +5,12 @@ import { Form } from "react-final-form";
|
||||
|
||||
import { InvalidRequestError } from "coral-framework/lib/errors";
|
||||
import { useMutation } from "coral-framework/lib/relay";
|
||||
import {
|
||||
Button,
|
||||
CallOut,
|
||||
HorizontalGutter,
|
||||
Typography,
|
||||
} from "coral-ui/components";
|
||||
import { Button, CallOut } from "coral-ui/components/v3";
|
||||
|
||||
import UnsubscribeNotificationsMutation from "./UnsubscribeNotificationsMutation";
|
||||
|
||||
import styles from "./Unsubscribe.css";
|
||||
|
||||
interface Props {
|
||||
token: string;
|
||||
disabled?: boolean;
|
||||
@@ -42,30 +39,34 @@ const UnsubscribeForm: React.FunctionComponent<Props> = ({
|
||||
<Form onSubmit={onSubmit}>
|
||||
{({ handleSubmit, submitting, submitError }) => (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<HorizontalGutter>
|
||||
<div>
|
||||
<Localized id="unsubscribe-unsubscribeFromEmails">
|
||||
<div className={styles.title}>
|
||||
Unsubscribe from email notifications
|
||||
</div>
|
||||
</Localized>
|
||||
<Localized id="unsubscribe-clickToConfirm">
|
||||
<Typography variant="heading1">
|
||||
<div className={styles.description}>
|
||||
Click below to confirm that you want to unsubscribe from all
|
||||
notifications.
|
||||
</Typography>
|
||||
</div>
|
||||
</Localized>
|
||||
{submitError && (
|
||||
<CallOut color="error" fullWidth>
|
||||
{submitError}
|
||||
</CallOut>
|
||||
)}
|
||||
<Localized id="unsubscribe-confirm">
|
||||
{submitError && <CallOut color="error" title={submitError} />}
|
||||
<Localized id="unsubscribe-submit-unsubscribe">
|
||||
<Button
|
||||
type="submit"
|
||||
variant="filled"
|
||||
paddingSize="medium"
|
||||
color="primary"
|
||||
disabled={submitting}
|
||||
upperCase
|
||||
fullWidth
|
||||
className={styles.submit}
|
||||
>
|
||||
Confirm
|
||||
Unsubscribe
|
||||
</Button>
|
||||
</Localized>
|
||||
</HorizontalGutter>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</Form>
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
.title {
|
||||
font-family: var(--font-family-secondary);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-secondary-bold);
|
||||
font-size: var(--font-size-6);
|
||||
line-height: 1.17;
|
||||
|
||||
color: var(--palette-text-500);
|
||||
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
.description {
|
||||
font-family: var(--font-family-primary);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-primary-regular);
|
||||
font-size: var(--font-size-3);
|
||||
line-height: 1.25;
|
||||
|
||||
color: var(--palette-text-500);
|
||||
|
||||
margin-bottom: var(--spacing-3);
|
||||
}
|
||||
|
||||
.label {
|
||||
font-family: var(--font-family-primary);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-primary-bold);
|
||||
font-size: var(--font-size-2);
|
||||
line-height: 1.14;
|
||||
|
||||
color: var(--palette-text-500);
|
||||
|
||||
margin-bottom: var(--spacing-1);
|
||||
}
|
||||
|
||||
.labelDescription {
|
||||
font-family: var(--font-family-primary);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-primary-regular);
|
||||
font-size: var(--font-size-2);
|
||||
line-height: 1.285;
|
||||
|
||||
color: var(--palette-text-100);
|
||||
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
.submit {
|
||||
margin-top: var(--spacing-5);
|
||||
margin-bottom: var(--spacing-1);
|
||||
}
|
||||
@@ -4,26 +4,20 @@ import React, { useCallback } from "react";
|
||||
import { Field, Form } from "react-final-form";
|
||||
|
||||
import { InvalidRequestError } from "coral-framework/lib/errors";
|
||||
import { colorFromMeta, ValidationMessage } from "coral-framework/lib/form";
|
||||
import { colorFromMeta } from "coral-framework/lib/form";
|
||||
import { useMutation } from "coral-framework/lib/relay";
|
||||
import {
|
||||
composeValidators,
|
||||
required,
|
||||
validatePassword,
|
||||
} from "coral-framework/lib/validation";
|
||||
import {
|
||||
Button,
|
||||
CallOut,
|
||||
FormField,
|
||||
HorizontalGutter,
|
||||
InputDescription,
|
||||
InputLabel,
|
||||
PasswordField,
|
||||
Typography,
|
||||
} from "coral-ui/components";
|
||||
import { FormField, PasswordField } from "coral-ui/components/v2";
|
||||
import { Button, CallOut, ValidationMessage } from "coral-ui/components/v3";
|
||||
|
||||
import ResetPasswordMutation from "./ResetPasswordMutation";
|
||||
|
||||
import styles from "./Reset.css";
|
||||
|
||||
interface Props {
|
||||
token: string;
|
||||
disabled?: boolean;
|
||||
@@ -60,22 +54,20 @@ const ResetPasswordForm: React.FunctionComponent<Props> = ({
|
||||
<Form onSubmit={onSubmit}>
|
||||
{({ handleSubmit, submitting, submitError }) => (
|
||||
<form autoComplete="off" onSubmit={handleSubmit}>
|
||||
<HorizontalGutter size="double">
|
||||
<HorizontalGutter>
|
||||
<div>
|
||||
<div>
|
||||
<Localized id="resetPassword-resetYourPassword">
|
||||
<Typography variant="heading1">
|
||||
Reset your password
|
||||
</Typography>
|
||||
<div className={styles.title}>Reset your password</div>
|
||||
</Localized>
|
||||
<Localized id="resetPassword-pleaseEnterNewPassword">
|
||||
<Typography variant="bodyCopy">
|
||||
<div className={styles.description}>
|
||||
Please enter a new password to use to sign in to your
|
||||
account. Make sure it is unique and be sure to keep it
|
||||
secure.
|
||||
</Typography>
|
||||
</div>
|
||||
</Localized>
|
||||
</HorizontalGutter>
|
||||
<HorizontalGutter>
|
||||
</div>
|
||||
<div>
|
||||
<Field
|
||||
name="password"
|
||||
validate={composeValidators(required, validatePassword)}
|
||||
@@ -83,15 +75,17 @@ const ResetPasswordForm: React.FunctionComponent<Props> = ({
|
||||
{({ input, meta }) => (
|
||||
<FormField>
|
||||
<Localized id="resetPassword-passwordLabel">
|
||||
<InputLabel htmlFor={input.name}>Password</InputLabel>
|
||||
<label className={styles.label} htmlFor={input.name}>
|
||||
Password
|
||||
</label>
|
||||
</Localized>
|
||||
<Localized
|
||||
id="resetPassword-passwordDescription"
|
||||
$minLength={8}
|
||||
>
|
||||
<InputDescription>
|
||||
<div className={styles.labelDescription}>
|
||||
{"Must be at least {$minLength} characters"}
|
||||
</InputDescription>
|
||||
</div>
|
||||
</Localized>
|
||||
<Localized
|
||||
id="resetPassword-passwordTextField"
|
||||
@@ -107,28 +101,27 @@ const ResetPasswordForm: React.FunctionComponent<Props> = ({
|
||||
{...input}
|
||||
/>
|
||||
</Localized>
|
||||
<ValidationMessage meta={meta} fullWidth />
|
||||
<ValidationMessage meta={meta} />
|
||||
</FormField>
|
||||
)}
|
||||
</Field>
|
||||
{submitError && (
|
||||
<CallOut color="error" fullWidth>
|
||||
{submitError}
|
||||
</CallOut>
|
||||
)}
|
||||
{submitError && <CallOut color="error" title={submitError} />}
|
||||
<Localized id="resetPassword-resetPassword">
|
||||
<Button
|
||||
type="submit"
|
||||
variant="filled"
|
||||
color="primary"
|
||||
paddingSize="medium"
|
||||
disabled={submitting}
|
||||
upperCase
|
||||
fullWidth
|
||||
className={styles.submit}
|
||||
>
|
||||
Reset Password
|
||||
</Button>
|
||||
</Localized>
|
||||
</HorizontalGutter>
|
||||
</HorizontalGutter>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</Form>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import React from "react";
|
||||
|
||||
import { CallOut, HorizontalGutter, Typography } from "coral-ui/components";
|
||||
import { CallOut } from "coral-ui/components/v3";
|
||||
|
||||
import styles from "./Reset.css";
|
||||
|
||||
interface Props {
|
||||
reason: React.ReactNode;
|
||||
@@ -9,23 +11,27 @@ interface Props {
|
||||
|
||||
const Sorry: React.FunctionComponent<Props> = ({ reason }) => {
|
||||
return (
|
||||
<HorizontalGutter size="double">
|
||||
<div>
|
||||
<Localized id="resetPassword-oopsSorry">
|
||||
<Typography variant="heading1">Oops Sorry!</Typography>
|
||||
<div className={styles.title}>Oops Sorry!</div>
|
||||
</Localized>
|
||||
<CallOut color="error" fullWidth>
|
||||
{reason ? (
|
||||
reason
|
||||
) : (
|
||||
<Localized id="account-tokenNotFound">
|
||||
<span data-testid="invalid-link">
|
||||
The specified link is invalid, check to see if it was copied
|
||||
correctly.
|
||||
</span>
|
||||
</Localized>
|
||||
)}
|
||||
</CallOut>
|
||||
</HorizontalGutter>
|
||||
<CallOut
|
||||
color="error"
|
||||
titleWeight="semiBold"
|
||||
title={
|
||||
reason ? (
|
||||
reason
|
||||
) : (
|
||||
<Localized id="account-tokenNotFound">
|
||||
<span data-testid="invalid-link">
|
||||
The specified link is invalid, check to see if it was copied
|
||||
correctly.
|
||||
</span>
|
||||
</Localized>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import React from "react";
|
||||
|
||||
import { HorizontalGutter, Typography } from "coral-ui/components";
|
||||
import styles from "./Reset.css";
|
||||
|
||||
const Success: React.FunctionComponent = () => {
|
||||
return (
|
||||
<HorizontalGutter size="double">
|
||||
<div>
|
||||
<Localized id="resetPassword-successfullyReset">
|
||||
<Typography variant="heading1">Password successfully reset</Typography>
|
||||
<div className={styles.title}>Password successfully reset</div>
|
||||
</Localized>
|
||||
<Localized id="resetPassword-youMayClose">
|
||||
<Typography variant="bodyCopy">
|
||||
<div className={styles.description}>
|
||||
You may now close this window and sign in to your account with your
|
||||
new password.
|
||||
</Typography>
|
||||
</div>
|
||||
</Localized>
|
||||
</HorizontalGutter>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -20,29 +20,22 @@ exports[`renders form 1`] = `
|
||||
autoComplete="off"
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-double"
|
||||
>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-full"
|
||||
>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
|
||||
<div>
|
||||
<div>
|
||||
<div
|
||||
className="Confirm-title"
|
||||
>
|
||||
Email Confirmation
|
||||
</h1>
|
||||
<p
|
||||
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary"
|
||||
Confirm your email address
|
||||
</div>
|
||||
<div
|
||||
className="Confirm-description"
|
||||
>
|
||||
Click below to confirm your email address.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-full"
|
||||
>
|
||||
<div>
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled Button-fullWidth"
|
||||
data-color="primary"
|
||||
className="BaseButton-root Button-base Button-filled Button-fontSizeSmall Button-textAlignCenter Button-fontFamilyPrimary Button-fontWeightPrimaryBold Button-paddingSizeSmall Button-colorPrimary Button-upperCase Button-fullWidth Confirm-submit"
|
||||
data-variant="filled"
|
||||
disabled={false}
|
||||
onBlur={[Function]}
|
||||
@@ -79,25 +72,34 @@ exports[`renders missing confirm token 1`] = `
|
||||
<div
|
||||
className="ConfirmRoute-root"
|
||||
>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-double"
|
||||
>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
|
||||
<div>
|
||||
<div
|
||||
className="Confirm-title"
|
||||
>
|
||||
Oops Sorry!
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
className="CallOut-root CallOut-colorError CallOut-fullWidth"
|
||||
className="CallOut-root CallOut-error CallOut-leftBorder"
|
||||
>
|
||||
<div
|
||||
className="CallOut-inner"
|
||||
className="Box-root Flex-root Flex-flex Flex-justifyFlexStart Flex-alignFlexStart"
|
||||
>
|
||||
<span
|
||||
data-testid="invalid-link"
|
||||
<div
|
||||
className="CallOut-content"
|
||||
>
|
||||
The specified link is invalid, check to see if it was copied correctly.
|
||||
</span>
|
||||
<div
|
||||
className="CallOut-title CallOut-titleSemiBold"
|
||||
>
|
||||
<span
|
||||
data-testid="invalid-link"
|
||||
>
|
||||
The specified link is invalid, check to see if it was copied correctly.
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="CallOut-body"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -21,41 +21,35 @@ exports[`renders form 1`] = `
|
||||
autoComplete="off"
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-double"
|
||||
>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-full"
|
||||
>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
|
||||
<div>
|
||||
<div>
|
||||
<div
|
||||
className="Reset-title"
|
||||
>
|
||||
Reset your password
|
||||
</h1>
|
||||
<p
|
||||
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary"
|
||||
</div>
|
||||
<div
|
||||
className="Reset-description"
|
||||
>
|
||||
Please enter a new password to use to sign in to your account.
|
||||
Make sure it is unique and be sure to keep it secure.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-full"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root FormField-root HorizontalGutter-half"
|
||||
className="Box-root HorizontalGutter-root FormField-root HorizontalGutter-spacing-2"
|
||||
>
|
||||
<label
|
||||
className="Box-root Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
|
||||
className="Reset-label"
|
||||
htmlFor="password"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
<p
|
||||
className="Box-root Typography-root Typography-fieldDescription Typography-colorTextSecondary"
|
||||
<div
|
||||
className="Reset-labelDescription"
|
||||
>
|
||||
Must be at least 8 characters
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className="PasswordField-fullWidth PasswordField-root"
|
||||
>
|
||||
@@ -67,6 +61,7 @@ Make sure it is unique and be sure to keep it secure.
|
||||
autoComplete="new-password"
|
||||
autoCorrect="off"
|
||||
className="PasswordField-colorRegular PasswordField-fullWidth PasswordField-input"
|
||||
data-testid="password-field"
|
||||
disabled={false}
|
||||
id="password"
|
||||
name="password"
|
||||
@@ -97,8 +92,7 @@ Make sure it is unique and be sure to keep it secure.
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled Button-fullWidth"
|
||||
data-color="primary"
|
||||
className="BaseButton-root Button-base Button-filled Button-fontSizeSmall Button-textAlignCenter Button-fontFamilyPrimary Button-fontWeightPrimaryBold Button-paddingSizeMedium Button-colorPrimary Button-upperCase Button-fullWidth Reset-submit"
|
||||
data-variant="filled"
|
||||
disabled={false}
|
||||
onBlur={[Function]}
|
||||
@@ -136,25 +130,34 @@ exports[`renders missing reset token 1`] = `
|
||||
<div
|
||||
className="ResetRoute-root"
|
||||
>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-double"
|
||||
>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
|
||||
<div>
|
||||
<div
|
||||
className="Reset-title"
|
||||
>
|
||||
Oops Sorry!
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
className="CallOut-root CallOut-colorError CallOut-fullWidth"
|
||||
className="CallOut-root CallOut-error CallOut-leftBorder"
|
||||
>
|
||||
<div
|
||||
className="CallOut-inner"
|
||||
className="Box-root Flex-root Flex-flex Flex-justifyFlexStart Flex-alignFlexStart"
|
||||
>
|
||||
<span
|
||||
data-testid="invalid-link"
|
||||
<div
|
||||
className="CallOut-content"
|
||||
>
|
||||
The specified link is invalid, check to see if it was copied correctly.
|
||||
</span>
|
||||
<div
|
||||
className="CallOut-title CallOut-titleSemiBold"
|
||||
>
|
||||
<span
|
||||
data-testid="invalid-link"
|
||||
>
|
||||
The specified link is invalid, check to see if it was copied correctly.
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="CallOut-body"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,17 +22,19 @@ exports[`renders form 1`] = `
|
||||
<form
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-full"
|
||||
>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
|
||||
<div>
|
||||
<div
|
||||
className="Unsubscribe-title"
|
||||
>
|
||||
Unsubscribe from email notifications
|
||||
</div>
|
||||
<div
|
||||
className="Unsubscribe-description"
|
||||
>
|
||||
Click below to confirm that you want to unsubscribe from all notifications.
|
||||
</h1>
|
||||
</div>
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled Button-fullWidth"
|
||||
data-color="primary"
|
||||
className="BaseButton-root Button-base Button-filled Button-fontSizeSmall Button-textAlignCenter Button-fontFamilyPrimary Button-fontWeightPrimaryBold Button-paddingSizeMedium Button-colorPrimary Button-upperCase Button-fullWidth Unsubscribe-submit"
|
||||
data-variant="filled"
|
||||
disabled={false}
|
||||
onBlur={[Function]}
|
||||
@@ -42,7 +44,7 @@ exports[`renders form 1`] = `
|
||||
onTouchEnd={[Function]}
|
||||
type="submit"
|
||||
>
|
||||
Confirm
|
||||
Unsubscribe
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -69,25 +71,34 @@ exports[`renders missing confirm token 1`] = `
|
||||
<div
|
||||
className="UnsubscribeRoute-root"
|
||||
>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-double"
|
||||
>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
|
||||
<div>
|
||||
<div
|
||||
className="Unsubscribe-title"
|
||||
>
|
||||
Oops Sorry!
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
className="CallOut-root CallOut-colorError CallOut-fullWidth"
|
||||
className="CallOut-root CallOut-error CallOut-leftBorder"
|
||||
>
|
||||
<div
|
||||
className="CallOut-inner"
|
||||
className="Box-root Flex-root Flex-flex Flex-justifyFlexStart Flex-alignFlexStart"
|
||||
>
|
||||
<span
|
||||
data-testid="invalid-link"
|
||||
<div
|
||||
className="CallOut-content"
|
||||
>
|
||||
The specified link is invalid, check to see if it was copied correctly.
|
||||
</span>
|
||||
<div
|
||||
className="CallOut-title CallOut-titleSemiBold"
|
||||
>
|
||||
<span
|
||||
data-testid="invalid-link"
|
||||
>
|
||||
The specified link is invalid, check to see if it was copied correctly.
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="CallOut-body"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -53,9 +53,7 @@ it("renders form", async () => {
|
||||
|
||||
await act(async () => {
|
||||
await waitForElement(() =>
|
||||
within(root).getByText("Email Confirmation", {
|
||||
exact: false,
|
||||
})
|
||||
within(root).getByText("Confirm your email address")
|
||||
);
|
||||
});
|
||||
expect(within(root).toJSON()).toMatchSnapshot();
|
||||
@@ -130,9 +128,7 @@ it("submits form", async () => {
|
||||
|
||||
await act(async () => {
|
||||
await waitForElement(() =>
|
||||
within(root).getByText("Email Confirmation", {
|
||||
exact: false,
|
||||
})
|
||||
within(root).getByText("Confirm your email address")
|
||||
);
|
||||
});
|
||||
const form = within(root).getByType("form");
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
.root {
|
||||
color: var(--v2-palette-success-main);
|
||||
color: var(--palette-success-500);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
}
|
||||
|
||||
.historyIcon {
|
||||
color: var(--v2-palette-text-secondary);
|
||||
color: var(--palette-text-100);
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.root {
|
||||
padding: var(--v2-spacing-2) var(--v2-spacing-1);
|
||||
padding: var(--spacing-2) var(--spacing-1);
|
||||
}
|
||||
|
||||
.leftCol {
|
||||
@@ -8,5 +8,5 @@
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
padding-top: 3px;
|
||||
margin-right: var(--v2-spacing-2);
|
||||
margin-right: var(--spacing-2);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
$decisionHistoryBorderColor: var(--v2-colors-grey-300);
|
||||
$decisionHistoryBorderColor: var(--palette-grey-300);
|
||||
|
||||
.root {
|
||||
margin: 0;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.root {
|
||||
height: 100%;
|
||||
font-size: var(--v2-font-size-1);
|
||||
line-height: var(--v2-line-height-reset);
|
||||
color: var(--v2-colors-mono-100);
|
||||
padding: 0 var(--v2-spacing-1);
|
||||
font-size: var(--font-size-1);
|
||||
line-height: 1.33;
|
||||
color: var(--palette-text-100);
|
||||
padding: 0 var(--spacing-1);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
}
|
||||
|
||||
.text {
|
||||
font-family: var(--v2-font-family-primary);
|
||||
font-weight: var(--v2-font-weight-primary-regular);
|
||||
font-size: var(--v2-font-size-2);
|
||||
line-height: var(--v2-line-height-body-short);
|
||||
font-family: var(--font-family-primary);
|
||||
font-weight: var(--font-weight-primary-regular);
|
||||
font-size: var(--font-size-2);
|
||||
line-height: 1.285;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
.root {
|
||||
margin-top: var(--v2-spacing-1);
|
||||
margin-top: var(--spacing-1);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
$moderateCardDecisionHistoryLinkTextColor: var(--v2-colors-teal-700);
|
||||
$moderateCardDecisionHistoryLinkTextColor: $colors-teal-700;
|
||||
|
||||
.root {
|
||||
line-height: var(--v2-line-height-reset);
|
||||
font-weight: var(--v2-font-weight-primary-bold);
|
||||
font-family: var(--v2-font-family-primary);
|
||||
font-size: var(--v2-font-size-1);
|
||||
font-weight: var(--font-weight-primary-bold);
|
||||
font-family: var(--font-family-primary);
|
||||
font-size: var(--font-size-1);
|
||||
line-height: 1.33;
|
||||
color: $moderateCardDecisionHistoryLinkTextColor;
|
||||
font-style: var(--v2-font-style-underline);
|
||||
font-style: var(--font-style-underline);
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
word-break: break-all;
|
||||
/* Fallback end */
|
||||
word-break: break-word;
|
||||
color: var(--v2-palette-text-primary);
|
||||
font-weight: var(--v2-font-weight-primary-semi-bold);
|
||||
font-family: var(--v2-font-family-primary);
|
||||
line-height: var(--v2-line-height-body-short);
|
||||
font-size: var(--v2-font-size-2);
|
||||
color: var(--palette-text-500);
|
||||
font-weight: var(--font-weight-primary-semi-bold);
|
||||
font-family: var(--font-family-primary);
|
||||
font-size: var(--font-size-2);
|
||||
line-height: 1.285;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
.root {
|
||||
color: var(--v2-palette-error-main);
|
||||
color: var(--palette-error-500);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
$moderateCardDecisionHistoryLinkTextColor: var(--v2-colors-teal-700);
|
||||
$moderateCardDecisionHistoryBorderColor: var(--v2-colors-grey-300);
|
||||
$moderateCardDecisionHistoryLinkTextColor: $colors-teal-700;
|
||||
$moderateCardDecisionHistoryBorderColor: var(--palette-grey-300);
|
||||
|
||||
.root {
|
||||
font-size: var(--v2-font-size-2);
|
||||
font-weight: var(--v2-font-weight-primary-bold);
|
||||
font-family: var(--v2-font-family-primary);
|
||||
line-height: var(--v2-line-height-reset);
|
||||
font-size: var(--font-size-2);
|
||||
font-weight: var(--font-weight-primary-bold);
|
||||
font-family: var(--font-family-primary);
|
||||
line-height: 1.14;
|
||||
text-transform: uppercase;
|
||||
color: $moderateCardDecisionHistoryLinkTextColor;
|
||||
width: 100%;
|
||||
border-top: 1px solid $moderateCardDecisionHistoryBorderColor;
|
||||
text-align: center;
|
||||
padding-top: var(--v2-spacing-1);
|
||||
padding-bottom: var(--v2-spacing-1);
|
||||
padding-top: var(--spacing-1);
|
||||
padding-bottom: var(--spacing-1);
|
||||
|
||||
&:disabled {
|
||||
color: var(--v2-palette-grey-lighter);
|
||||
color: var(--palette-grey-300);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
$timestampColor: var(--v2-colors-mono-100);
|
||||
$timestampColor: var(--palette-text-100);
|
||||
|
||||
.root {
|
||||
color: $timestampColor;
|
||||
font-family: var(--v2-font-family-primary);
|
||||
font-weight: var(--v2-font-weight-primary-bold);
|
||||
font-size: var(--v2-font-size-1);
|
||||
line-height: var(--v2-line-height-reset);
|
||||
font-family: var(--font-family-primary);
|
||||
font-weight: var(--font-weight-primary-bold);
|
||||
font-size: var(--font-size-1);
|
||||
line-height: 1.33;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
$decisionHistoryBorderColor: var(--v2-colors-grey-300);
|
||||
$decisionHistoryTitleBackgroundColor: var(--v2-colors-grey-200);
|
||||
$decisionHistoryBorderColor: var(--palette-grey-300);
|
||||
$decisionHistoryTitleBackgroundColor: var(--palette-grey-200);
|
||||
|
||||
.root {
|
||||
background: $decisionHistoryTitleBackgroundColor;
|
||||
padding: var(--v2-spacing-1);
|
||||
color: var(--v2-palette-text-primary);
|
||||
padding: var(--spacing-1);
|
||||
color: var(--palette-text-500);
|
||||
border-bottom: 1px solid $decisionHistoryBorderColor;
|
||||
font-size: var(--v2-font-size-icon-medium);
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: var(--v2-spacing-2);
|
||||
font-size: var(--font-size-icon-md);
|
||||
margin-right: var(--spacing-2);
|
||||
}
|
||||
|
||||
.text {
|
||||
text-transform: uppercase;
|
||||
font-weight: var(--v2-font-weight-primary-bold);
|
||||
color: var(--v2-palette-text-primary);
|
||||
font-size: var(--v2-font-size-2);
|
||||
font-weight: var(--font-weight-primary-bold);
|
||||
color: var(--palette-text-500);
|
||||
font-size: var(--font-size-2);
|
||||
line-height: 1;
|
||||
font-family: var(--v2-font-family-primary);
|
||||
font-family: var(--font-family-primary);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
$moderateCardDecisionHistoryColor: var(--v2-colors-mono-900);
|
||||
$moderateCardDecisionHistoryColor: var(--palette-text-900);
|
||||
|
||||
.root {
|
||||
color: $moderateCardDecisionHistoryColor;
|
||||
font-weight: var(--v2-font-weight-secondary-bold);
|
||||
font-family: var(--v2-font-family-secondary);
|
||||
line-height: var(--v2-line-height-body-short);
|
||||
font-size: var(--v2-font-size-3);
|
||||
font-weight: var(--font-weight-secondary-bold);
|
||||
font-family: var(--font-family-secondary);
|
||||
line-height: 1.3;
|
||||
font-size: var(--font-size-3);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import useNotification from "./useNotification";
|
||||
|
||||
const NotificationContainer: FunctionComponent<{}> = () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { PropTypesOf } from "coral-framework/types";
|
||||
import { LogoHorizontal } from "coral-ui/components";
|
||||
import { LogoHorizontal } from "coral-ui/components/v2";
|
||||
import { AppBar, Begin, Divider, End } from "coral-ui/components/v2/AppBar";
|
||||
|
||||
import { DecisionHistoryButton } from "./DecisionHistory";
|
||||
|
||||
@@ -5,6 +5,6 @@ import Navigation from "./Navigation";
|
||||
|
||||
it("renders correctly", () => {
|
||||
const renderer = createRenderer();
|
||||
renderer.render(<Navigation showConfigure />);
|
||||
renderer.render(<Navigation showConfigure showDashboard={true} />);
|
||||
expect(renderer.getRenderOutput()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ import NavigationLink from "./NavigationLink";
|
||||
|
||||
interface Props {
|
||||
showConfigure: boolean;
|
||||
showDashboard: boolean;
|
||||
}
|
||||
|
||||
const Navigation: FunctionComponent<Props> = (props) => (
|
||||
@@ -25,6 +26,11 @@ const Navigation: FunctionComponent<Props> = (props) => (
|
||||
<NavigationLink to="/admin/configure">Configure</NavigationLink>
|
||||
</Localized>
|
||||
)}
|
||||
{props.showDashboard && (
|
||||
<Localized id="navigation-dashboard">
|
||||
<NavigationLink to="/admin/dashboard">Dashboard</NavigationLink>
|
||||
</Localized>
|
||||
)}
|
||||
</AppBarNavigation>
|
||||
);
|
||||
|
||||
|
||||
@@ -21,6 +21,9 @@ class NavigationContainer extends React.Component<Props> {
|
||||
public render() {
|
||||
return (
|
||||
<Navigation
|
||||
showDashboard={
|
||||
!!this.props.viewer && can(this.props.viewer, Ability.VIEW_STATISTICS)
|
||||
}
|
||||
showConfigure={
|
||||
!!this.props.viewer &&
|
||||
can(this.props.viewer, Ability.CHANGE_CONFIGURATION)
|
||||
|
||||
@@ -38,5 +38,14 @@ exports[`renders correctly 1`] = `
|
||||
Configure
|
||||
</NavigationLink>
|
||||
</Localized>
|
||||
<Localized
|
||||
id="navigation-dashboard"
|
||||
>
|
||||
<NavigationLink
|
||||
to="/admin/dashboard"
|
||||
>
|
||||
Dashboard
|
||||
</NavigationLink>
|
||||
</Localized>
|
||||
</withPropsOnChange(Navigation)>
|
||||
`;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
$userMenuButtonColor: var(--v2-colors-mono-100);
|
||||
$userMenuButtonColor: var(--palette-text-100);
|
||||
|
||||
.button {
|
||||
padding-top: 0;
|
||||
@@ -11,9 +11,9 @@ $userMenuButtonColor: var(--v2-colors-mono-100);
|
||||
}
|
||||
|
||||
.buttonText {
|
||||
font-family: var(--v2-font-family-secondary);
|
||||
font-family: var(--font-family-secondary);
|
||||
color: $userMenuButtonColor;
|
||||
font-size: var(--v2-font-size-4);
|
||||
font-size: var(--font-size-4);
|
||||
margin-left: 2px;
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.version {
|
||||
font-size: calc(10rem / var(--rem-base));
|
||||
color: var(--palette-text-secondary);
|
||||
font-size: 0.625rem;
|
||||
color: var(--palette-text-100);
|
||||
text-align: center;
|
||||
padding-bottom: var(--spacing-3);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { Typography } from "coral-ui/components";
|
||||
import { Typography } from "coral-ui/components/v2";
|
||||
|
||||
import styles from "./Version.css";
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
.container {
|
||||
width: 385px;
|
||||
background-color: var(--v2-colors-grey-100);
|
||||
border: 1px solid var(--v2-colors-grey-300);
|
||||
background-color: var(--palette-grey-100);
|
||||
border: 1px solid var(--palette-grey-300);
|
||||
margin-top: 70px;
|
||||
padding: var(--v2-spacing-6);
|
||||
padding: var(--spacing-6);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
$comment-content: var(--v2-colors-mono-900);
|
||||
$comment-blockquote-bg: var(--v2-colors-grey-100);
|
||||
$comment-highlight-bg: var(--v2-colors-yellow-500);
|
||||
$comment-link: var(--v2-palette-primary-main);
|
||||
$comment-link-active: var(--v2-palette-primary-darkest);
|
||||
$comment-content: var(--palette-text-900);
|
||||
$comment-blockquote-bg: var(--palette-grey-100);
|
||||
$comment-highlight-bg: var(--palette-warning-500);
|
||||
$comment-link: $colors-teal-700;
|
||||
$comment-link-active: $colors-teal-900;
|
||||
|
||||
.root {
|
||||
composes: bodyCommentV2 from "~coral-ui/shared/typography.css";
|
||||
font-family: var(--v2-font-family-primary);
|
||||
font-size: var(--v2-font-size-3);
|
||||
line-height: var(--v2-line-height-body-comment);
|
||||
font-size: var(--font-size-3);
|
||||
font-weight: var(--font-weight-primary-regular);
|
||||
font-family: var(--font-family-primary);
|
||||
line-height: 1.45;
|
||||
color: $comment-content;
|
||||
overflow-wrap: break-word;
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: var(--v2-font-weight-primary-bold);
|
||||
font-weight: var(--font-weight-primary-bold);
|
||||
}
|
||||
i,
|
||||
em {
|
||||
|
||||
@@ -1,20 +1,50 @@
|
||||
import cn from "classnames";
|
||||
import React, { FunctionComponent, useMemo } from "react";
|
||||
import striptags from "striptags";
|
||||
|
||||
import {
|
||||
getPhrasesRegExp,
|
||||
GetPhrasesRegExpOptions,
|
||||
markHTMLNode,
|
||||
} from "coral-admin/helpers";
|
||||
import { createPurify } from "coral-common/utils/purify";
|
||||
import {
|
||||
ALL_FEATURES,
|
||||
createSanitize,
|
||||
Sanitize,
|
||||
} from "coral-common/helpers/sanitize";
|
||||
|
||||
import styles from "./CommentContent.css";
|
||||
|
||||
/**
|
||||
* Create a purify instance that will be used to handle HTML content.
|
||||
* Return a purify instance that will be used to handle HTML content.
|
||||
*/
|
||||
const purify = createPurify(window, false);
|
||||
const getSanitize: (highlight: boolean) => Sanitize = (() => {
|
||||
let sanitizers: Record<"default" | "highlight", Sanitize> | null = null;
|
||||
return (highlight: boolean) => {
|
||||
if (!sanitizers) {
|
||||
sanitizers = {
|
||||
default: createSanitize(window, {
|
||||
// Allow all RTE features to be displayed.
|
||||
features: ALL_FEATURES,
|
||||
}),
|
||||
highlight: createSanitize(window, {
|
||||
// We need normalized text nodes to mark nodes for suspect/banned words.
|
||||
normalize: true,
|
||||
// Allow all RTE features to be displayed.
|
||||
features: ALL_FEATURES,
|
||||
config: {
|
||||
FORBID_TAGS: highlight
|
||||
? ["b", "strong", "i", "em", "s", "span"]
|
||||
: [],
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
if (highlight) {
|
||||
return sanitizers.highlight!;
|
||||
}
|
||||
return sanitizers.default!;
|
||||
};
|
||||
})();
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
@@ -51,15 +81,7 @@ const CommentContent: FunctionComponent<Props> = ({
|
||||
}
|
||||
|
||||
// Sanitize the input for display.
|
||||
let html = purify.sanitize(children);
|
||||
if (highlight) {
|
||||
html = striptags(html, ["a"]);
|
||||
}
|
||||
|
||||
// We create a Shadow DOM Tree with the HTML body content and use it as a
|
||||
// parser.
|
||||
const node = document.createElement("div");
|
||||
node.innerHTML = html;
|
||||
const node = getSanitize(highlight)(children);
|
||||
|
||||
// If the expression is available, then mark the nodes.
|
||||
if (expression) {
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
$moderateCardIconColor: var(--v2-colors-mono-100);
|
||||
$moderateCardInReplyToColor: var(--v2-colors-mono-100);
|
||||
$moderateCardInReplyToUsernameColor: var(--v2-colors-mono-500);
|
||||
$moderateCardUsernameBackgroundHover: var(--v2-colors-grey-200);
|
||||
$moderateCardIconColor: var(--palette-text-100);
|
||||
$moderateCardInReplyToColor: var(--palette-text-100);
|
||||
$moderateCardInReplyToUsernameColor: var(--palette-text-500);
|
||||
$moderateCardUsernameBackgroundHover: var(--palette-grey-200);
|
||||
|
||||
.icon {
|
||||
color: $moderateCardIconColor;
|
||||
}
|
||||
.inReplyTo {
|
||||
color: $moderateCardInReplyToColor;
|
||||
font-family: var(--v2-font-family-primary);
|
||||
font-weight: var(--v2-font-weight-primary-regular);
|
||||
font-size: var(--v2-font-size-2);
|
||||
line-height: var(--v2-line-height-reset);
|
||||
font-family: var(--font-family-primary);
|
||||
font-weight: var(--font-weight-primary-regular);
|
||||
font-size: var(--font-size-2);
|
||||
line-height: 1.14;
|
||||
}
|
||||
.username {
|
||||
color: $moderateCardInReplyToUsernameColor;
|
||||
font-family: var(--v2-font-family-secondary);
|
||||
font-weight: var(--v2-font-weight-secondary-bold);
|
||||
font-size: var(--v2-font-size-3);
|
||||
line-height: var(--v2-line-height-reset);
|
||||
font-family: var(--font-family-secondary);
|
||||
font-weight: var(--font-weight-secondary-bold);
|
||||
font-size: var(--font-size-3);
|
||||
line-height: 1;
|
||||
|
||||
padding: var(--spacing-1);
|
||||
|
||||
@@ -38,5 +38,5 @@ $moderateCardUsernameBackgroundHover: var(--v2-colors-grey-200);
|
||||
.usernameButton {
|
||||
vertical-align: baseline;
|
||||
|
||||
margin-left: calc(-1 * var(--v2-spacing-1));
|
||||
margin-left: calc(-1 * var(--spacing-1));
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
$moderateCardUsernameColor: var(--v2-colors-mono-900);
|
||||
$moderateCardUsernameColor: var(--palette-text-900);
|
||||
|
||||
.root {
|
||||
font-size: var(--v2-font-size-3);
|
||||
font-weight: var(--v2-font-weight-secondary-bold);
|
||||
font-family: var(--v2-font-family-secondary);
|
||||
line-height: var(--v2-line-height-reset);
|
||||
font-size: var(--font-size-3);
|
||||
font-weight: var(--font-weight-secondary-bold);
|
||||
font-family: var(--font-family-secondary);
|
||||
line-height: 1;
|
||||
color: $moderateCardUsernameColor;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
$moderateCardUsernameBackgroundHover: var(--v2-colors-grey-200);
|
||||
$moderateCardUsernameBackgroundHover: var(--palette-grey-200);
|
||||
|
||||
.root {
|
||||
margin-right: var(--mini-unit);
|
||||
padding: var(--spacing-1);
|
||||
margin-left: calc(-1 * var(--v2-spacing-1));
|
||||
margin-left: calc(-1 * var(--spacing-1));
|
||||
|
||||
line-height: var(--v2-line-height-reset);
|
||||
line-height: 1;
|
||||
|
||||
&:hover {
|
||||
background-color: $moderateCardUsernameBackgroundHover;
|
||||
border-radius: var(--v2-round-corners);
|
||||
border-radius: var(--round-corners);
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: $moderateCardUsernameBackgroundHover;
|
||||
border-radius: var(--v2-round-corners);
|
||||
border-radius: var(--round-corners);
|
||||
border-style: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import cn from "classnames";
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { BaseButton } from "coral-ui/components/v2";
|
||||
|
||||
import Username from "./Username";
|
||||
|
||||
import styles from "./UsernameButton.css";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { Modal } from "coral-ui/components";
|
||||
import { Modal } from "coral-ui/components/v2";
|
||||
|
||||
import ConversationModalQuery from "./ConversationModalQuery";
|
||||
|
||||
|
||||
+11
-11
@@ -1,31 +1,31 @@
|
||||
$conversationModalHighlightBackground: var(--v2-colors-teal-100);
|
||||
$conversationModalCommentText: var(--v2-colors-mono-500);
|
||||
$conversationModalHighlightBackground: $colors-teal-100;
|
||||
$conversationModalCommentText: var(--palette-text-500);
|
||||
.root {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.line {
|
||||
border-color: var(--v2-colors-grey-400);
|
||||
border-color: var(--palette-grey-400);
|
||||
}
|
||||
|
||||
.adornments {
|
||||
margin-right: var(--v2-spacing-2);
|
||||
margin-right: var(--spacing-2);
|
||||
}
|
||||
|
||||
.circle {
|
||||
margin-top: var(--v2-spacing-2);
|
||||
margin-bottom: var(--v2-spacing-2);
|
||||
margin-top: var(--spacing-2);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
.highlightedCircle {
|
||||
margin-top: var(--v2-spacing-1);
|
||||
margin-top: var(--spacing-1);
|
||||
}
|
||||
|
||||
.highlighted {
|
||||
background-color: $conversationModalHighlightBackground;
|
||||
padding: var(--v2-spacing-2);
|
||||
padding-top: var(--v2-spacing-1);
|
||||
margin-bottom: var(--v2-spacing-2);
|
||||
padding: var(--spacing-2);
|
||||
padding-top: var(--spacing-1);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
.commentText {
|
||||
@@ -33,5 +33,5 @@ $conversationModalCommentText: var(--v2-colors-mono-500);
|
||||
}
|
||||
|
||||
.showReplies {
|
||||
padding-left: var(--v2-spacing-2);
|
||||
padding-left: var(--spacing-2);
|
||||
}
|
||||
|
||||
+1
-1
@@ -104,7 +104,7 @@ const ConversationModalCommentContainer: FunctionComponent<Props> = ({
|
||||
<Localized id="conversation-modal-reply-show-replies">
|
||||
<Button
|
||||
color="mono"
|
||||
variant="outline"
|
||||
variant="outlined"
|
||||
onClick={onShowReplies}
|
||||
fullWidth
|
||||
>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
.root {
|
||||
}
|
||||
.topCircle {
|
||||
margin-right: var(--v2-spacing-2);
|
||||
margin-right: var(--spacing-2);
|
||||
}
|
||||
|
||||
.bottomCircleContainer {
|
||||
width: 8px;
|
||||
padding-top: var(--v2-spacing-1);
|
||||
padding-top: var(--spacing-1);
|
||||
}
|
||||
|
||||
+7
-7
@@ -1,17 +1,17 @@
|
||||
$modal-header-text: var(--v2-colors-mono-900);
|
||||
$modal-header-text: var(--palette-text-900);
|
||||
|
||||
.title {
|
||||
font-size: var(--v2-font-size-4);
|
||||
font-family: var(--v2-font-family-primary);
|
||||
font-weight: var(--v2-font-weight-primary-regular);
|
||||
line-height: var(--v2-line-height-title);
|
||||
font-size: var(--font-size-4);
|
||||
font-family: var(--font-family-primary);
|
||||
font-weight: var(--font-weight-primary-regular);
|
||||
line-height: 1.11;
|
||||
color: $modal-header-text;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.conversationTitle {
|
||||
font-weight: var(--v2-font-weight-primary-semi-bold);
|
||||
font-weight: var(--font-weight-primary-semi-bold);
|
||||
text-transform: uppercase;
|
||||
display: block;
|
||||
margin-bottom: var(--v2-spacing-2);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
+6
-1
@@ -39,7 +39,12 @@ const ConversationModalHeaderContainer: FunctionComponent<Props> = ({
|
||||
: comment.story.url}
|
||||
</h1>
|
||||
<Localized id="conversation-modal-header-moderate-link">
|
||||
<Button variant="outline" color="mono" uppercase onClick={onModerate}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="mono"
|
||||
uppercase
|
||||
onClick={onModerate}
|
||||
>
|
||||
Moderate story
|
||||
</Button>
|
||||
</Localized>
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
}
|
||||
|
||||
.body {
|
||||
padding: var(--v2-spacing-4);
|
||||
padding: var(--spacing-4);
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
.comment {
|
||||
padding-left: calc(var(--v2-spacing-4));
|
||||
padding-left: calc(var(--spacing-4));
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding-left: calc(var(--v2-spacing-4));
|
||||
padding-left: calc(var(--spacing-4));
|
||||
}
|
||||
|
||||
+2
-2
@@ -62,7 +62,7 @@ const ConversationModalRepliesContainer: FunctionComponent<Props> = ({
|
||||
<div className={styles.footer}>
|
||||
{!showReplies && comment.replyCount > 0 && (
|
||||
<Localized id="conversation-modal-replies-show">
|
||||
<Button variant="outline" fullWidth onClick={onShowReplies}>
|
||||
<Button variant="outlined" fullWidth onClick={onShowReplies}>
|
||||
Show replies
|
||||
</Button>
|
||||
</Localized>
|
||||
@@ -71,7 +71,7 @@ const ConversationModalRepliesContainer: FunctionComponent<Props> = ({
|
||||
comment.replyCount > replies.length &&
|
||||
replies.length > 0 && (
|
||||
<Localized id="conversation-modal-replies-show-more">
|
||||
<Button variant="outline" fullWidth onClick={loadMore}>
|
||||
<Button variant="outlined" fullWidth onClick={loadMore}>
|
||||
Show more replies
|
||||
</Button>
|
||||
</Localized>
|
||||
|
||||
+5
-9
@@ -1,5 +1,5 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import React, { FunctionComponent, useMemo } from "react";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { QueryRenderData, QueryRenderer } from "coral-framework/lib/relay";
|
||||
@@ -60,17 +60,13 @@ const ConversationModalRepliesQuery: FunctionComponent<Props> = ({
|
||||
);
|
||||
}
|
||||
|
||||
const replies = useMemo(
|
||||
() => props.comment!.replies.edges.map((edge) => edge.node),
|
||||
[props.comment.replies]
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
{replies.map((reply) => (
|
||||
<div key={reply.id}>
|
||||
{props.comment.replies.edges.map((reply) => (
|
||||
<div key={reply.node.id}>
|
||||
<ConversationModalCommentContainer
|
||||
key={reply.id}
|
||||
comment={reply}
|
||||
key={reply.node.id}
|
||||
comment={reply.node}
|
||||
settings={props.settings}
|
||||
isHighlighted={false}
|
||||
isReply={true}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
$modal-body-text: $colors-mono-500;
|
||||
|
||||
.root {
|
||||
font-size: var(--font-size-3);
|
||||
font-family: var(--font-family-primary);
|
||||
font-weight: var(--font-weight-primary-regular);
|
||||
line-height: 1.3;
|
||||
color: $modal-body-text;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import styles from "./ModalBodyText.css";
|
||||
|
||||
const ModalBodyText: FunctionComponent = ({ children }) => (
|
||||
<p className={styles.root}>{children}</p>
|
||||
);
|
||||
|
||||
export default ModalBodyText;
|
||||
@@ -0,0 +1,10 @@
|
||||
$modal-text: $colors-mono-500;
|
||||
|
||||
.root {
|
||||
font-size: var(--font-size-5);
|
||||
font-family: var(--font-family-primary);
|
||||
font-weight: var(--font-weight-primary-semi-bold);
|
||||
line-height: 1.2;
|
||||
color: $modal-text;
|
||||
margin: 0;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import React, { FunctionComponent, HTMLAttributes } from "react";
|
||||
|
||||
import styles from "./ModalHeader.css";
|
||||
|
||||
const ModalHeader: FunctionComponent<HTMLAttributes<HTMLHeadingElement>> = ({
|
||||
children,
|
||||
...rest
|
||||
}) => {
|
||||
return (
|
||||
<h2 {...rest} className={styles.root}>
|
||||
{children}
|
||||
</h2>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModalHeader;
|
||||
@@ -0,0 +1,8 @@
|
||||
$status-modal-username: $colors-mono-900;
|
||||
|
||||
.root {
|
||||
font-weight: var(--font-weight-secondary-bold);
|
||||
font-family: var(--font-family-secondary);
|
||||
color: $status-modal-username;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
+1
-1
@@ -2,7 +2,7 @@ import React, { FunctionComponent } from "react";
|
||||
|
||||
import styles from "./ModalHeaderUsername.css";
|
||||
|
||||
const ModalHeaderUsername: FunctionComponent<{}> = ({ children }) => {
|
||||
const ModalHeaderUsername: FunctionComponent = ({ children }) => {
|
||||
return <strong className={styles.root}>{children}</strong>;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
$moderateCardButtonOutlineApproveColor: var(--v2-colors-green-500);
|
||||
$moderateCardButtonOutlineApproveColor: var(--palette-success-500);
|
||||
|
||||
.root {
|
||||
border: 1px solid $moderateCardButtonOutlineApproveColor;
|
||||
box-sizing: border-box;
|
||||
border-radius: var(--v2-round-corners);
|
||||
border-radius: var(--round-corners);
|
||||
width: 65px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
@@ -12,16 +12,37 @@ $moderateCardButtonOutlineApproveColor: var(--v2-colors-green-500);
|
||||
color: $moderateCardButtonOutlineApproveColor;
|
||||
&:not(:disabled):active {
|
||||
background-color: $moderateCardButtonOutlineApproveColor;
|
||||
color: var(--v2-colors-pure-white);
|
||||
color: var(--palette-text-000);
|
||||
}
|
||||
}
|
||||
|
||||
.readOnly {
|
||||
background-color: transparent;
|
||||
border-color: $colors-grey-300);
|
||||
color: $colors-grey-300);
|
||||
|
||||
&:hover {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.readOnly {
|
||||
background-color: transparent;
|
||||
border-color: $colors-grey-300;
|
||||
color: $colors-grey-300;
|
||||
|
||||
&:hover {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.invert {
|
||||
background-color: $moderateCardButtonOutlineApproveColor;
|
||||
color: var(--v2-colors-pure-white);
|
||||
border-color: $moderateCardButtonOutlineApproveColor;
|
||||
color: var(--palette-text-000);
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-weight: var(--v2-font-weight-primary-semi-bold);
|
||||
font-weight: var(--font-weight-primary-semi-bold);
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@@ -9,10 +9,12 @@ import styles from "./ApproveButton.css";
|
||||
|
||||
interface Props extends Omit<PropTypesOf<typeof BaseButton>, "ref"> {
|
||||
invert?: boolean;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
const ApproveButton: FunctionComponent<Props> = ({
|
||||
invert,
|
||||
readOnly,
|
||||
className,
|
||||
...rest
|
||||
}) => (
|
||||
@@ -21,6 +23,7 @@ const ApproveButton: FunctionComponent<Props> = ({
|
||||
{...rest}
|
||||
className={cn(className, styles.root, {
|
||||
[styles.invert]: invert,
|
||||
[styles.readOnly]: readOnly,
|
||||
})}
|
||||
aria-label="Approve"
|
||||
>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
.authorStatus {
|
||||
padding-right: var(--spacing-2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
right: 0px;
|
||||
top: 2px;
|
||||
|
||||
border: 1px solid var(--v2-palette-grey-main);
|
||||
border: 1px solid var(--palette-grey-500);
|
||||
box-sizing: border-box;
|
||||
border-radius: var(--round-corners);
|
||||
padding: 2px var(--spacing-2) 2px var(--spacing-2);
|
||||
@@ -11,35 +11,34 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
color: var(--v2-palette-grey-main);
|
||||
font-size: var(--v2-font-size-2);
|
||||
font-weight: var(--v2-font-weight-primary-regular);
|
||||
font-family: var(--v2-font-family-primary);
|
||||
color: var(--palette-grey-500);
|
||||
font-size: var(--font-size-2);
|
||||
font-weight: var(--font-weight-primary-regular);
|
||||
font-family: var(--font-family-primary);
|
||||
line-height: 1.2;
|
||||
|
||||
&:active {
|
||||
background-color: var(--v2-palette-primary-lightest);
|
||||
border-color: var(--v2-palette-primary-main);
|
||||
color: var(--v2-palette-primary-main);
|
||||
background-color: $colors-teal-100;
|
||||
border-color: $colors-teal-700;
|
||||
color: $colors-teal-700;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: default;
|
||||
|
||||
cursor: not-allowed;
|
||||
background-color: transparent;
|
||||
border-color: var(--v2-colors-grey-300);
|
||||
color: var(--v2-colors-grey-300);
|
||||
border-color: var(--palette-grey-300);
|
||||
color: var(--palette-grey-300);
|
||||
}
|
||||
}
|
||||
|
||||
.invert {
|
||||
background-color: var(--v2-palette-primary-main);
|
||||
border-color: var(--v2-palette-primary-main);
|
||||
color: var(--v2-palette-text-light);
|
||||
background-color: $colors-teal-700;
|
||||
border-color: $colors-teal-700;
|
||||
color: var(--palette-text-000);
|
||||
|
||||
&:disabled {
|
||||
color: var(--v2-colors-pure-white);
|
||||
border-color: var(--v2-colors-grey-300);
|
||||
background-color: var(--v2-colors-grey-300);
|
||||
color: var(--palette-text-000);
|
||||
border-color: var(--palette-grey-300);
|
||||
background-color: var(--palette-grey-300);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import NotAvailable from "coral-admin/components/NotAvailable";
|
||||
|
||||
import FlagDetailsCategory from "./FlagDetailsCategory";
|
||||
import FlagDetailsEntry from "./FlagDetailsEntry";
|
||||
|
||||
interface Props {
|
||||
category: React.ReactNode;
|
||||
nodes: ReadonlyArray<{
|
||||
flagger: { id: string; username: string | null } | null;
|
||||
additionalDetails: string | null;
|
||||
}>;
|
||||
onUsernameClick: (id?: string) => void;
|
||||
}
|
||||
|
||||
const FlagDetails: FunctionComponent<Props> = ({
|
||||
category,
|
||||
nodes,
|
||||
onUsernameClick,
|
||||
}) => {
|
||||
if (nodes.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<FlagDetailsCategory category={category}>
|
||||
{nodes.map((flag, i) => (
|
||||
<FlagDetailsEntry
|
||||
key={i}
|
||||
onClick={() =>
|
||||
flag.flagger ? onUsernameClick(flag.flagger.id) : null
|
||||
}
|
||||
user={
|
||||
flag.flagger && flag.flagger.username ? (
|
||||
flag.flagger.username
|
||||
) : (
|
||||
<NotAvailable />
|
||||
)
|
||||
}
|
||||
details={flag.additionalDetails}
|
||||
/>
|
||||
))}
|
||||
</FlagDetailsCategory>
|
||||
);
|
||||
};
|
||||
|
||||
export default FlagDetails;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user