|
-
+
{metric.name}
|
diff --git a/frontend/src/metabase/admin/datamodel/components/SegmentItem.jsx b/frontend/src/metabase/admin/datamodel/components/SegmentItem.jsx
index d1925cdf7f00..7d06f6095d0b 100644
--- a/frontend/src/metabase/admin/datamodel/components/SegmentItem.jsx
+++ b/frontend/src/metabase/admin/datamodel/components/SegmentItem.jsx
@@ -25,8 +25,8 @@ export default class SegmentItem extends Component {
{segment.name}
diff --git a/frontend/src/metabase/collections/components/BaseTableItem.jsx b/frontend/src/metabase/collections/components/BaseTableItem.jsx
index 4f343cb982e7..ab8f2437e874 100644
--- a/frontend/src/metabase/collections/components/BaseTableItem.jsx
+++ b/frontend/src/metabase/collections/components/BaseTableItem.jsx
@@ -89,7 +89,7 @@ export function BaseTableItem({
))}
diff --git a/frontend/src/metabase/entities/collections.js b/frontend/src/metabase/entities/collections.js
index 39ee6955d83a..ef1bfe412cf2 100644
--- a/frontend/src/metabase/entities/collections.js
+++ b/frontend/src/metabase/entities/collections.js
@@ -79,7 +79,7 @@ const Collections = createEntity({
objectSelectors: {
getName: collection => collection && collection.name,
getUrl: collection => Urls.collection(collection),
- getIcon: collection => "folder",
+ getIcon: collection => ({ name: "folder" }),
},
selectors: {
diff --git a/frontend/src/metabase/entities/dashboards.js b/frontend/src/metabase/entities/dashboards.js
index 94a7e3337ecf..c7c011d1359d 100644
--- a/frontend/src/metabase/entities/dashboards.js
+++ b/frontend/src/metabase/entities/dashboards.js
@@ -133,7 +133,7 @@ const Dashboards = createEntity({
getUrl: dashboard => dashboard && Urls.dashboard(dashboard),
getCollection: dashboard =>
dashboard && normalizedCollection(dashboard.collection),
- getIcon: dashboard => "dashboard",
+ getIcon: dashboard => ({ name: "dashboard" }),
getColor: () => color("dashboard"),
},
diff --git a/frontend/src/metabase/entities/databases.js b/frontend/src/metabase/entities/databases.js
index 5b2874c97680..a60e4115e340 100644
--- a/frontend/src/metabase/entities/databases.js
+++ b/frontend/src/metabase/entities/databases.js
@@ -66,7 +66,7 @@ const Databases = createEntity({
objectSelectors: {
getName: db => db && db.name,
getUrl: db => db && Urls.browseDatabase(db),
- getIcon: db => "database",
+ getIcon: db => ({ name: "database" }),
getColor: db => color("database"),
},
diff --git a/frontend/src/metabase/entities/metrics.js b/frontend/src/metabase/entities/metrics.js
index de9bfcc1c62f..eb10ec9ec90e 100644
--- a/frontend/src/metabase/entities/metrics.js
+++ b/frontend/src/metabase/entities/metrics.js
@@ -28,7 +28,7 @@ const Metrics = createEntity({
getUrl: metric =>
Urls.tableRowsQuery(metric.database_id, metric.table_id, metric.id),
getColor: metric => color("accent1"),
- getIcon: metric => "sum",
+ getIcon: metric => ({ name: "sum" }),
},
selectors: {
diff --git a/frontend/src/metabase/entities/pulses.js b/frontend/src/metabase/entities/pulses.js
index 8845f407a6ef..ac4e141aa70e 100644
--- a/frontend/src/metabase/entities/pulses.js
+++ b/frontend/src/metabase/entities/pulses.js
@@ -40,7 +40,7 @@ const Pulses = createEntity({
objectSelectors: {
getName: pulse => pulse && pulse.name,
getUrl: pulse => pulse && Urls.pulse(pulse.id),
- getIcon: pulse => "pulse",
+ getIcon: pulse => ({ name: "pulse" }),
getColor: pulse => color("pulse"),
},
diff --git a/frontend/src/metabase/entities/questions.js b/frontend/src/metabase/entities/questions.js
index 4886e58aea49..ec70eef840b2 100644
--- a/frontend/src/metabase/entities/questions.js
+++ b/frontend/src/metabase/entities/questions.js
@@ -68,9 +68,11 @@ const Questions = createEntity({
getColor: () => color("text-medium"),
getCollection: question =>
question && normalizedCollection(question.collection),
- getIcon: question =>
- (require("metabase/visualizations").default.get(question.display) || {})
- .iconName || "beaker",
+ getIcon: question => ({
+ name:
+ (require("metabase/visualizations").default.get(question.display) || {})
+ .iconName || "beaker",
+ }),
},
reducer: (state = {}, { type, payload, error }) => {
diff --git a/frontend/src/metabase/entities/segments.js b/frontend/src/metabase/entities/segments.js
index 3ed2e76f015c..6d68f1c1d27b 100644
--- a/frontend/src/metabase/entities/segments.js
+++ b/frontend/src/metabase/entities/segments.js
@@ -37,7 +37,7 @@ const Segments = createEntity({
segment.id,
),
getColor: segment => color("accent7"),
- getIcon: segment => "segment",
+ getIcon: segment => ({ name: "segment" }),
},
form: {
diff --git a/frontend/src/metabase/entities/snippet-collections.js b/frontend/src/metabase/entities/snippet-collections.js
index cf3ef624f86d..6b63506c6d9c 100644
--- a/frontend/src/metabase/entities/snippet-collections.js
+++ b/frontend/src/metabase/entities/snippet-collections.js
@@ -63,7 +63,7 @@ const SnippetCollections = createEntity({
}),
objectSelectors: {
- getIcon: collection => "folder",
+ getIcon: collection => ({ name: "folder" }),
},
form: {
diff --git a/frontend/src/metabase/entities/tables.js b/frontend/src/metabase/entities/tables.js
index 048aa60434a3..aac2fc78727d 100644
--- a/frontend/src/metabase/entities/tables.js
+++ b/frontend/src/metabase/entities/tables.js
@@ -180,7 +180,7 @@ const Tables = createEntity({
objectSelectors: {
getUrl: table =>
Urls.tableRowsQuery(table.database_id, table.table_id, null),
- getIcon: table => "table",
+ getIcon: table => ({ name: "table" }),
getColor: table => color("accent2"),
},
diff --git a/frontend/src/metabase/home/containers/ArchiveApp.jsx b/frontend/src/metabase/home/containers/ArchiveApp.jsx
index f1b53bed229b..6fe7bb664846 100644
--- a/frontend/src/metabase/home/containers/ArchiveApp.jsx
+++ b/frontend/src/metabase/home/containers/ArchiveApp.jsx
@@ -64,7 +64,7 @@ export default class ArchiveApp extends Component {
diff --git a/frontend/src/metabase/search/components/SearchResult.info.js b/frontend/src/metabase/search/components/SearchResult.info.js
index f26f351fe3ac..99228b92a363 100644
--- a/frontend/src/metabase/search/components/SearchResult.info.js
+++ b/frontend/src/metabase/search/components/SearchResult.info.js
@@ -9,7 +9,7 @@ const COLLECTION_EXAMPLE = {
model: "collection",
id: 1,
name: "Revenue",
- getIcon: () => "folder",
+ getIcon: () => ({ name: "folder" }),
};
const DASHBOARD_EXAMPLE = {
@@ -22,7 +22,7 @@ const DASHBOARD_EXAMPLE = {
id: "root",
name: "Our analytics",
},
- getIcon: () => "dashboard",
+ getIcon: () => ({ name: "dashboard" }),
};
const QUESTION_EXAMPLE = {
@@ -30,7 +30,7 @@ const QUESTION_EXAMPLE = {
id: 1,
name: "Revenue by region",
collection: COLLECTION_EXAMPLE,
- getIcon: () => "table",
+ getIcon: () => ({ name: "table" }),
};
const LONG_TITLE_DASHBOARD_EXAMPLE = {
diff --git a/frontend/src/metabase/search/components/SearchResult.jsx b/frontend/src/metabase/search/components/SearchResult.jsx
index 27c5c8e0e0f3..c5f1d61e790a 100644
--- a/frontend/src/metabase/search/components/SearchResult.jsx
+++ b/frontend/src/metabase/search/components/SearchResult.jsx
@@ -91,7 +91,7 @@ function ItemIcon({ item, type }) {
{type === "table" ? (
) : (
-
+
)}
);
diff --git a/frontend/test/metabase/collections/BaseItemsTable.unit.spec.js b/frontend/test/metabase/collections/BaseItemsTable.unit.spec.js
index 026815831739..9068f9117fc4 100644
--- a/frontend/test/metabase/collections/BaseItemsTable.unit.spec.js
+++ b/frontend/test/metabase/collections/BaseItemsTable.unit.spec.js
@@ -27,7 +27,7 @@ describe("Collections BaseItemsTable", () => {
last_name: "Doe",
timestamp: timestamp,
},
- getIcon: () => "dashboard",
+ getIcon: () => ({ name: "dashboard" }),
getUrl: () => "/dashboard/1",
};
From 233f8fdbcaa6386dbe43dafb53b183b32aba155b Mon Sep 17 00:00:00 2001
From: Anton Kulyk
Date: Fri, 16 Jul 2021 08:42:33 +0300
Subject: [PATCH 049/664] Allow displaying info tooltips on FormFields (#17073)
---
.../metabase/components/form/FormField.jsx | 34 +++++++++++++------
.../components/form/FormField.styled.js | 23 +++++++++++++
2 files changed, 46 insertions(+), 11 deletions(-)
create mode 100644 frontend/src/metabase/components/form/FormField.styled.js
diff --git a/frontend/src/metabase/components/form/FormField.jsx b/frontend/src/metabase/components/form/FormField.jsx
index 6b82e7cafca9..945ef58f70de 100644
--- a/frontend/src/metabase/components/form/FormField.jsx
+++ b/frontend/src/metabase/components/form/FormField.jsx
@@ -1,9 +1,12 @@
/* eslint-disable react/prop-types */
import React, { Component } from "react";
import PropTypes from "prop-types";
-
import cx from "classnames";
+import Tooltip from "metabase/components/Tooltip";
+
+import { FieldRow, Label, InfoIcon } from "./FormField.styled";
+
export default class FormField extends Component {
static propTypes = {
field: PropTypes.object,
@@ -18,6 +21,7 @@ export default class FormField extends Component {
hidden: PropTypes.bool,
title: PropTypes.string,
description: PropTypes.string,
+ info: PropTypes.string,
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
@@ -31,6 +35,7 @@ export default class FormField extends Component {
formField,
title = formField && formField.title,
description = formField && formField.description,
+ info = formField && formField.info,
hidden = formField &&
(formField.hidden != null
? formField.hidden
@@ -66,16 +71,23 @@ export default class FormField extends Component {
>
{(title || description) && (
- {title && (
-
- )}
+
+ {title && (
+
+ )}
+ {info && (
+
+
+
+ )}
+
{description && {description} }
)}
diff --git a/frontend/src/metabase/components/form/FormField.styled.js b/frontend/src/metabase/components/form/FormField.styled.js
new file mode 100644
index 000000000000..656369536b3e
--- /dev/null
+++ b/frontend/src/metabase/components/form/FormField.styled.js
@@ -0,0 +1,23 @@
+import styled from "styled-components";
+import Icon from "metabase/components/Icon";
+
+import { color } from "metabase/lib/colors";
+
+export const FieldRow = styled.div`
+ display: flex;
+ align-items: center;
+ margin-bottom: 0.5em;
+`;
+
+export const Label = styled.label`
+ margin-bottom: 0;
+`;
+
+export const InfoIcon = styled(Icon).attrs({ name: "info", size: 12 })`
+ margin-left: 8px;
+ color: ${color("bg-dark")};
+
+ &:hover {
+ color: ${color("brand")};
+ }
+`;
From 4452329a604f372d9fd6451e9db7327e86ed11b3 Mon Sep 17 00:00:00 2001
From: Gustavo Saiani
Date: Fri, 16 Jul 2021 13:37:32 -0300
Subject: [PATCH 050/664] Add propTypes, remove Flow types from
ParameterFieldWidget component (#17081)
---
.../widgets/ParameterFieldWidget.jsx | 51 +++++++------------
1 file changed, 17 insertions(+), 34 deletions(-)
diff --git a/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget.jsx b/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget.jsx
index f05803ac56bb..b44dde8b9036 100644
--- a/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget.jsx
+++ b/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget.jsx
@@ -1,5 +1,6 @@
import React, { Component } from "react";
import ReactDOM from "react-dom";
+import PropTypes from "prop-types";
import { t, ngettext, msgid } from "ttag";
import _ from "underscore";
@@ -9,11 +10,6 @@ import Popover from "metabase/components/Popover";
import Button from "metabase/components/Button";
import Value from "metabase/components/Value";
-import Field from "metabase-lib/lib/metadata/Field";
-
-import type { Parameter } from "metabase-types/types/Parameter";
-import type { DashboardWithCards } from "metabase-types/types/Dashboard";
-import type { FilterOperator } from "metabase-types/types/Metadata";
import cx from "classnames";
import {
getFilterArgumentFormatOptions,
@@ -21,26 +17,17 @@ import {
isFuzzyOperator,
} from "metabase/lib/schema_metadata";
-type Props = {
- value: any,
- setValue: () => void,
-
- isEditing: boolean,
-
- fields: Field[],
- parentFocusChanged: boolean => void,
-
- operator?: FilterOperator,
- dashboard?: DashboardWithCards,
- parameter?: Parameter,
- parameters?: Parameter[],
- placeholder?: string,
-};
-
-type State = {
- value: any[],
- isFocused: boolean,
- widgetWidth: ?number,
+const propTypes = {
+ dashboard: PropTypes.object,
+ fields: PropTypes.array.isRequired,
+ isEditing: PropTypes.bool.isRequired,
+ operator: PropTypes.object.isRequired,
+ parameter: PropTypes.object.isRequired,
+ parameters: PropTypes.array.isRequired,
+ parentFocusChanged: PropTypes.bool,
+ placeholder: PropTypes.string.isRequired,
+ setValue: PropTypes.func.isRequired,
+ value: PropTypes.string,
};
const BORDER_WIDTH = 1;
@@ -48,14 +35,8 @@ const BORDER_WIDTH = 1;
const normalizeValue = value =>
Array.isArray(value) ? value : value != null ? [value] : [];
-// TODO: rename this something else since we're using it for more than searching and more than text
-export default class ParameterFieldWidget extends Component<*, Props, State> {
- props: Props;
- state: State;
-
- _unfocusedElement: React.Component;
-
- constructor(props: Props) {
+export default class ParameterFieldWidget extends Component {
+ constructor(props) {
super(props);
this.state = {
isFocused: false,
@@ -84,7 +65,7 @@ export default class ParameterFieldWidget extends Component<*, Props, State> {
}
}
- UNSAFE_componentWillReceiveProps(nextProps: Props) {
+ UNSAFE_componentWillReceiveProps(nextProps) {
if (this.props.value !== nextProps.value) {
this.setState({ value: nextProps.value });
}
@@ -216,3 +197,5 @@ export default class ParameterFieldWidget extends Component<*, Props, State> {
}
}
}
+
+ParameterFieldWidget.propTypes = propTypes;
From 64062e8752d783d8aa0126c564e0d428248fe050 Mon Sep 17 00:00:00 2001
From: dpsutton
Date: Mon, 19 Jul 2021 08:52:43 -0500
Subject: [PATCH 051/664] offset is a reserved word in mariadb as of 10.6
(#17101)
* offset is a reserved word in mariadb as of 10.6
- https://mariadb.com/kb/en/reserved-words/
- https://github.com/MariaDB/server/commit/299b935320
I think it is related to https://jira.mariadb.org/browse/MDEV-23908
"Implement SELECT ... OFFSET ... FETCH ..."
* Handle new names for utf8 in mariadb
https://mariadb.com/kb/en/unicode/
- utf8: Until MariaDB 10.5, this was a UTF-8 encoding using one to
three bytes per character. Basic Latin letters, numbers and
punctuation use one byte. European and Middle East letters mostly fit
into 2 bytes. Korean, Chinese, and Japanese ideographs use 3-bytes. No
supplementary characters are stored. From MariaDB 10.6, utf8 is an
alias for utf8mb3, but this can changed to ut8mb4 by changing the
default value of the old_mode system variable.
- utf8mb3: UTF-8 encoding using one to three bytes per
character. Basic Latin letters, numbers and punctuation use one
byte. European and Middle East letters mostly fit into 2
bytes. Korean, Chinese, and Japanese ideographs use 3-bytes. No
supplementary characters are stored. Until MariaDB 10.5, this was an
alias for utf8. From MariaDB 10.6, utf8 is by default an alias for
utf8mb3, but this can changed to ut8mb4 by changing the default value
of the old_mode system variable.
---
src/metabase/driver/mysql.clj | 2 +-
test/metabase/db/fix_mysql_utf8_test.clj | 14 +++++++++-----
2 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/src/metabase/driver/mysql.clj b/src/metabase/driver/mysql.clj
index 6b6d51477aef..26969f8dd3ca 100644
--- a/src/metabase/driver/mysql.clj
+++ b/src/metabase/driver/mysql.clj
@@ -133,7 +133,7 @@
" now(), convert_tz(now(), @@GLOBAL.time_zone, '+00:00')"
" ),"
" '%H:%i'"
- " ) AS offset;")
+ " ) AS 'offset';")
[{:keys [global_tz system_tz offset]}] (jdbc/query spec sql)
the-valid-id (fn [zone-id]
(when zone-id
diff --git a/test/metabase/db/fix_mysql_utf8_test.clj b/test/metabase/db/fix_mysql_utf8_test.clj
index 2d477133bb03..0597e829a24f 100644
--- a/test/metabase/db/fix_mysql_utf8_test.clj
+++ b/test/metabase/db/fix_mysql_utf8_test.clj
@@ -86,11 +86,15 @@
(remove-utf8mb4-migrations! jdbc-spec)
(binding [db/*db-connection* jdbc-spec]
(testing (format "DB without migrations 107-160: UTF-8 shouldn't work when using the '%s' character set" charset)
- (is (= {:character-set charset, :collation collation}
- (db-charset)
- (table-charset)
- (column-charset))
- (format "Make sure we converted the DB to %s correctly" charset))
+ (let [db-cs (db-charset)
+ tb-cs (table-charset)
+ col-cs (column-charset)]
+ (is (every? (cond-> #{charset} (= charset "utf8") (conj "utf8mb3"))
+ (map :character-set [db-cs tb-cs col-cs]))
+ (format "Make sure we converted the DB to %s correctly" charset))
+ (is (every? (cond-> #{collation} (= collation "utf8_general_ci") (conj "utf8mb3_general_ci"))
+ (map :collation [db-cs tb-cs col-cs]))
+ (format "Make sure we converted the DB to %s correctly" charset)))
(is (thrown?
Exception
(insert-row!))
From 3fd87094bae6c32850d39e4d5b4198a4833310a9 Mon Sep 17 00:00:00 2001
From: Noah Moss <32746338+noahmoss@users.noreply.github.com>
Date: Mon, 19 Jul 2021 10:22:07 -0700
Subject: [PATCH 052/664] Link to MB docs from Google Sign-In admin page
instead of Google docs (#17075)
---
.../admin/settings/components/SettingsGoogleForm.jsx | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm.jsx b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm.jsx
index b805663c78ee..17a6dd5b3195 100644
--- a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm.jsx
+++ b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm.jsx
@@ -14,6 +14,7 @@ import { updateSettings } from "metabase/admin/settings/settings";
import { settingToFormField } from "metabase/admin/settings/utils";
import Breadcrumbs from "metabase/components/Breadcrumbs";
import ExternalLink from "metabase/components/ExternalLink";
+import MetabaseSettings from "metabase/lib/settings";
const settingsGoogleFormPropTypes = {
elements: PropTypes.array,
@@ -56,7 +57,10 @@ export default class SettingsGoogleForm extends Component {
{jt`To allow users to sign in with Google you'll need to give Metabase a Google Developers console application client ID. It only takes a few steps and instructions on how to create a key can be found ${(
{t`here`}
From 2448065e92d814b116472bf560f5af518d7f4ed1 Mon Sep 17 00:00:00 2001
From: Ariya Hidayat
Date: Mon, 19 Jul 2021 11:24:00 -0700
Subject: [PATCH 053/664] Separate the (whitelabel) color theme tests (#17106)
This is for easier troubleshooting (rerun from failed, separated
recording) since those tests are quite hard to run smoothly with Circle
CI.
Also, move away from using the API (/api/setting/application-colors)
and just use the color picker from the UI to choose invididual custom color.
---
.../whitelabel-color-theme.cy.spec.js | 124 ++++++++++++++++++
.../admin/settings/whitelabel.cy.spec.js | 48 -------
2 files changed, 124 insertions(+), 48 deletions(-)
create mode 100644 frontend/test/metabase/scenarios/admin/settings/whitelabel-color-theme.cy.spec.js
diff --git a/frontend/test/metabase/scenarios/admin/settings/whitelabel-color-theme.cy.spec.js b/frontend/test/metabase/scenarios/admin/settings/whitelabel-color-theme.cy.spec.js
new file mode 100644
index 000000000000..bddadefa7983
--- /dev/null
+++ b/frontend/test/metabase/scenarios/admin/settings/whitelabel-color-theme.cy.spec.js
@@ -0,0 +1,124 @@
+import { restore, describeWithToken } from "__support__/e2e/cypress";
+
+// Define colors that we use for whitelabeling
+// If rbg values exist, it's because we explicit test those
+const colors = {
+ primary: { hex: "8B572A", rgb: [139, 87, 42] },
+ nav: { hex: "284E07", rgb: [40, 78, 7] },
+ accent1: { hex: "417505" },
+ accent2: { hex: "7ED321" },
+ additional1: { hex: "B8E986" },
+ additional2: { hex: "50E3C2" },
+ additional3: { hex: "4A90E2" },
+ additional4: { hex: "082CBE" },
+ additional5: { hex: "F8E71C", rgb: [248, 231, 28] },
+};
+
+function changeThemeColor(location, colorhex) {
+ cy.get("td")
+ .eq(location)
+ .click();
+ cy.get(`div[title='#${colorhex}']`).click();
+ cy.findByText("Done").click();
+}
+
+describeWithToken("formatting > whitelabel > color theme", () => {
+ beforeEach(() => {
+ restore();
+ cy.signInAsAdmin();
+ });
+
+ it("should change the brand color", () => {
+ cy.visit("/admin/settings/whitelabel");
+ cy.intercept("GET", `/api/setting`).as("setting");
+ cy.intercept("GET", "/api/session/properties").as("sessionProperties");
+
+ // brand color
+ changeThemeColor(1, colors.primary.hex);
+ cy.wait("@setting");
+
+ cy.wait("@sessionProperties").then(xhr => {
+ console.log(xhr.response.body);
+ console.log(xhr.response.body["application-colors"]);
+ });
+
+ cy.signOut();
+
+ cy.visit("/");
+
+ // "Remember me" checkbox has to have the branded background color
+ cy.get('div span[size="16"]').should(
+ "have.css",
+ "background-color",
+ `rgb(${colors.primary.rgb.join(", ")})`,
+ );
+ });
+
+ it("should change the navigation color", () => {
+ cy.visit("/admin/settings/whitelabel");
+ cy.intercept("GET", `/api/setting`).as("setting");
+ cy.intercept("GET", "/api/session/properties").as("sessionProperties");
+
+ // brand color
+ cy.get("td")
+ .eq(5)
+ .click();
+ cy.get(".sketch-picker")
+ .find("input")
+ .first()
+ .clear()
+ .type(colors.nav.hex);
+ cy.findByText("Done").click();
+ cy.wait("@setting");
+
+ cy.wait("@sessionProperties").then(xhr => {
+ console.log(xhr.response.body);
+ console.log(xhr.response.body["application-colors"]);
+ });
+
+ cy.signOut();
+
+ cy.visit("/");
+
+ cy.log("Normal users should have a green header");
+ cy.signInAsNormalUser();
+ cy.visit("/");
+ cy.get(".Nav").should(
+ "have.css",
+ "background-color",
+ `rgb(${colors.nav.rgb.join(", ")})`,
+ );
+ });
+
+ it("should change the special navigation bar in the admin panel", () => {
+ cy.visit("/admin/settings/whitelabel");
+ cy.intercept("GET", `/api/setting`).as("setting");
+ cy.intercept("GET", "/api/session/properties").as("sessionProperties");
+
+ // admin nav
+ cy.get("td")
+ .eq(33)
+ .click();
+ cy.get(".sketch-picker")
+ .find("input")
+ .first()
+ .clear()
+ .type(colors.additional5.hex);
+ cy.findByText("Done").click();
+ cy.wait("@setting");
+
+ cy.wait("@sessionProperties").then(xhr => {
+ console.log(xhr.response.body);
+ console.log(xhr.response.body["application-colors"]);
+ });
+
+ cy.scrollTo("top");
+
+ cy.log("Admin panel should be yellow");
+ cy.get(".Nav").should(
+ "have.css",
+ "background-color",
+ `rgb(${colors.additional5.rgb.join(", ")})`,
+ );
+ });
+});
diff --git a/frontend/test/metabase/scenarios/admin/settings/whitelabel.cy.spec.js b/frontend/test/metabase/scenarios/admin/settings/whitelabel.cy.spec.js
index 66e19a031015..78d7a3c8864f 100644
--- a/frontend/test/metabase/scenarios/admin/settings/whitelabel.cy.spec.js
+++ b/frontend/test/metabase/scenarios/admin/settings/whitelabel.cy.spec.js
@@ -139,54 +139,6 @@ describeWithToken("formatting > whitelabel", () => {
});
});
- it("should reflect color changes", () => {
- cy.signOut();
- cy.visit("/");
-
- // Note that if we have modified the logo, the entire background turns the brand color.
- // But if we _haven't_, as is the case now, then the existing logo is branded
- // As is the "Remember me" and "Sign in" inputs
- cy.get(".Icon.text-brand").should(
- "have.css",
- "color",
- `rgb(${colors.primary.rgb.join(", ")})`,
- );
-
- cy.findByLabelText("Email address").type("some@email.test");
- cy.findByLabelText("Password").type("1234");
- cy.get(".Button--primary").should(
- "have.css",
- "background-color",
- `rgb(${colors.primary.rgb.join(", ")})`,
- );
-
- cy.log("Normal users should have a green header");
- cy.signInAsNormalUser();
- cy.visit("/");
- cy.get(".Nav").should(
- "have.css",
- "background-color",
- `rgb(${colors.nav.rgb.join(", ")})`,
- );
-
- cy.log(
- "Admin users should also have a green header, but yellow in the admin panel",
- );
- cy.signInAsAdmin();
- cy.visit("/");
- cy.get(".Nav").should(
- "have.css",
- "background-color",
- `rgb(${colors.nav.rgb.join(", ")})`,
- );
- cy.visit("/admin");
- cy.get(".Nav").should(
- "have.css",
- "background-color",
- `rgb(${colors.additional5.rgb.join(", ")})`,
- );
- });
-
it.skip("should show color changes reflected in q visualizations (metabase-enterprise #470)", () => {
// *** Test should pass when issue #470 is resolved
cy.signInAsNormalUser();
From ae384492763468af3b09e70272227875e4ad43ab Mon Sep 17 00:00:00 2001
From: Anton Kulyk
Date: Mon, 19 Jul 2021 21:27:52 +0300
Subject: [PATCH 054/664] Fix missing icons (#17088)
---
frontend/src/metabase/containers/ItemPicker.jsx | 2 +-
.../dashboard/components/add-card-sidebar/QuestionPicker.jsx | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/frontend/src/metabase/containers/ItemPicker.jsx b/frontend/src/metabase/containers/ItemPicker.jsx
index ca31b84f9bc5..d3b490c10fde 100644
--- a/frontend/src/metabase/containers/ItemPicker.jsx
+++ b/frontend/src/metabase/containers/ItemPicker.jsx
@@ -206,7 +206,7 @@ export default class ItemPicker extends React.Component {
item={collection}
name={collection.name}
color={COLLECTION_ICON_COLOR}
- icon={getCollectionIcon(collection)}
+ icon={getCollectionIcon(collection).name}
selected={canSelect && isSelected(collection)}
canSelect={canSelect}
hasChildren={hasChildren}
diff --git a/frontend/src/metabase/dashboard/components/add-card-sidebar/QuestionPicker.jsx b/frontend/src/metabase/dashboard/components/add-card-sidebar/QuestionPicker.jsx
index 0619c9db03b7..ba8bdd20bc30 100644
--- a/frontend/src/metabase/dashboard/components/add-card-sidebar/QuestionPicker.jsx
+++ b/frontend/src/metabase/dashboard/components/add-card-sidebar/QuestionPicker.jsx
@@ -84,7 +84,7 @@ function QuestionPicker({
key={collection.id}
id={collection.id}
name={collection.name}
- icon={getCollectionIcon(collection)}
+ icon={getCollectionIcon(collection).name}
onSelect={collectionId => setCurrentCollectionId(collectionId)}
/>
))}
From 6a6cdfd737c7517c5d7ea8ba8cec587ea7d38bd8 Mon Sep 17 00:00:00 2001
From: Anton Kulyk
Date: Mon, 19 Jul 2021 21:28:51 +0300
Subject: [PATCH 055/664] Fix SearchResult.info page broken (#17086)
---
.../search/components/SearchResult.info.js | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/frontend/src/metabase/search/components/SearchResult.info.js b/frontend/src/metabase/search/components/SearchResult.info.js
index 99228b92a363..961f00993027 100644
--- a/frontend/src/metabase/search/components/SearchResult.info.js
+++ b/frontend/src/metabase/search/components/SearchResult.info.js
@@ -5,11 +5,15 @@ export const category = "search";
export const description = `Displays search results w/ optional context in typeahead and on the search results page`;
+const DEMO_URL = "/_internal/components/searchresult";
+
const COLLECTION_EXAMPLE = {
model: "collection",
id: 1,
name: "Revenue",
getIcon: () => ({ name: "folder" }),
+ getUrl: () => DEMO_URL,
+ getCollection: () => {},
};
const DASHBOARD_EXAMPLE = {
@@ -23,6 +27,8 @@ const DASHBOARD_EXAMPLE = {
name: "Our analytics",
},
getIcon: () => ({ name: "dashboard" }),
+ getUrl: () => DEMO_URL,
+ getCollection: () => COLLECTION_EXAMPLE,
};
const QUESTION_EXAMPLE = {
@@ -31,6 +37,8 @@ const QUESTION_EXAMPLE = {
name: "Revenue by region",
collection: COLLECTION_EXAMPLE,
getIcon: () => ({ name: "table" }),
+ getUrl: () => DEMO_URL,
+ getCollection: () => COLLECTION_EXAMPLE,
};
const LONG_TITLE_DASHBOARD_EXAMPLE = {
@@ -41,10 +49,12 @@ const LONG_TITLE_DASHBOARD_EXAMPLE = {
const QUESTION_CONTEXT_EXAMPLE = {
...QUESTION_EXAMPLE,
name: "Poorly named item",
- context: {
- match: "description",
- content: "This is actually about Revenue",
- },
+ context: [
+ {
+ match: "description",
+ content: "This is actually about Revenue",
+ },
+ ],
};
export const examples = {
From 6aaf5c4ff1f9aa00b09f09cb13468cd80c82fc10 Mon Sep 17 00:00:00 2001
From: Anton Kulyk
Date: Mon, 19 Jul 2021 21:29:08 +0300
Subject: [PATCH 056/664] Use `saturated-yellow` instead of `warning` (#17085)
---
frontend/src/metabase/search/components/SearchResult.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frontend/src/metabase/search/components/SearchResult.jsx b/frontend/src/metabase/search/components/SearchResult.jsx
index c5f1d61e790a..706a376f8691 100644
--- a/frontend/src/metabase/search/components/SearchResult.jsx
+++ b/frontend/src/metabase/search/components/SearchResult.jsx
@@ -18,7 +18,7 @@ import Table from "metabase/entities/tables";
function getColorForIconWrapper(props) {
if (props.item.collection_position) {
- return color("warning");
+ return color("saturated-yellow");
}
switch (props.type) {
case "collection":
From e01d0c7d5b2ce8ee2fcc5d6b60714a14a8d7f940 Mon Sep 17 00:00:00 2001
From: Anton Kulyk
Date: Mon, 19 Jul 2021 21:29:26 +0300
Subject: [PATCH 057/664] Replace ButtonGroup component with SegmentedControl
(#16958)
* Add icons-only mode to SegmentedControl
* Add `fullWidth` mode to SegmentedControl
* Use SegmentedControl at SchedulePicker
* Use SegmentedControl at EmailAttachmentPicker
* Use SegmentedControl at ChartNestedSettingSeries
* Replace ChartSettingButtonGroup with SegmentedControl
* Remove ButtonGroup component
* Don't show labels for some viz settings
* Increase SegmentedControl's click area
* Add `inactiveColor` prop to SegmentedControl
* Fix icons-only SegmentedControl padding
---
.../src/metabase/components/ButtonGroup.jsx | 46 -------------------
.../metabase/components/SchedulePicker.jsx | 5 +-
.../components/SegmentedControl.info.js | 8 ++++
.../metabase/components/SegmentedControl.jsx | 33 +++++++++----
.../components/SegmentedControl.styled.js | 34 +++++++++++---
.../components/EmailAttachmentPicker.jsx | 5 +-
.../settings/ChartNestedSettingSeries.jsx | 13 ++++--
.../settings/ChartSettingButtonGroup.jsx | 17 -------
.../settings/ChartSettingSegmentedControl.jsx | 8 ++++
.../metabase/visualizations/lib/settings.js | 4 +-
.../visualizations/lib/settings/graph.js | 7 +--
.../visualizations/lib/settings/series.js | 12 ++---
12 files changed, 90 insertions(+), 102 deletions(-)
delete mode 100644 frontend/src/metabase/components/ButtonGroup.jsx
delete mode 100644 frontend/src/metabase/visualizations/components/settings/ChartSettingButtonGroup.jsx
create mode 100644 frontend/src/metabase/visualizations/components/settings/ChartSettingSegmentedControl.jsx
diff --git a/frontend/src/metabase/components/ButtonGroup.jsx b/frontend/src/metabase/components/ButtonGroup.jsx
deleted file mode 100644
index c085d2cf17ad..000000000000
--- a/frontend/src/metabase/components/ButtonGroup.jsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import React from "react";
-
-import cx from "classnames";
-
-type Value = any;
-type Option = any;
-
-type Props = {
- value: Value,
- onChange: (value: Value) => void,
- options: Option[],
- optionNameFn?: (o: Option) => string | React.Element,
- optionValueFn?: (o: Option) => Value,
- optionKeyFn?: (o: Option) => string,
- className?: string,
-};
-
-const ButtonGroup = ({
- value,
- onChange,
- options,
- optionNameFn = o => o.name,
- optionValueFn = o => o.value,
- optionKeyFn = optionValueFn,
- className,
-}: Props) => {
- return (
-
- {options.map((o, index) => (
- 0 },
- optionValueFn(o) === value ? "text-brand" : "text-medium",
- )}
- onClick={() => onChange(optionValueFn(o))}
- >
- {optionNameFn(o)}
-
- ))}
-
- );
-};
-
-export default ButtonGroup;
diff --git a/frontend/src/metabase/components/SchedulePicker.jsx b/frontend/src/metabase/components/SchedulePicker.jsx
index b286ff7b4c86..5c93b96419e5 100644
--- a/frontend/src/metabase/components/SchedulePicker.jsx
+++ b/frontend/src/metabase/components/SchedulePicker.jsx
@@ -2,7 +2,7 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
-import ButtonGroup from "metabase/components/ButtonGroup";
+import { SegmentedControl } from "metabase/components/SegmentedControl";
import Select from "metabase/components/Select";
import Settings from "metabase/lib/settings";
@@ -238,12 +238,13 @@ export default class SchedulePicker extends Component {
this.handleChangeProperty("schedule_hour", value + amPm * 12)
}
/>
-
this.handleChangeProperty("schedule_hour", hour + value * 12)
}
options={AM_PM_OPTIONS}
+ fullWidth
/>
{textBeforeSendTime && (
diff --git a/frontend/src/metabase/components/SegmentedControl.info.js b/frontend/src/metabase/components/SegmentedControl.info.js
index 23635465955e..491e786691ea 100644
--- a/frontend/src/metabase/components/SegmentedControl.info.js
+++ b/frontend/src/metabase/components/SegmentedControl.info.js
@@ -19,6 +19,12 @@ const OPTIONS_WITH_ICONS = [
{ name: "Doohickey", value: 2, icon: "insight" },
];
+const OPTIONS_ICONS_ONLY = [
+ { value: 0, icon: "lightbulb" },
+ { value: 1, icon: "folder" },
+ { value: 2, icon: "insight" },
+];
+
const OPTIONS_WITH_COLORS = [
{
name: "Gadget",
@@ -44,5 +50,7 @@ function SegmentedControlDemo(props) {
export const examples = {
default: ,
icons: ,
+ iconsOnly: ,
colored: ,
+ fullWidth: ,
};
diff --git a/frontend/src/metabase/components/SegmentedControl.jsx b/frontend/src/metabase/components/SegmentedControl.jsx
index 1d42d47d7fd3..bae6ac3e74ab 100644
--- a/frontend/src/metabase/components/SegmentedControl.jsx
+++ b/frontend/src/metabase/components/SegmentedControl.jsx
@@ -1,15 +1,16 @@
import React, { useMemo } from "react";
import PropTypes from "prop-types";
import _ from "underscore";
-import Icon from "metabase/components/Icon";
import {
SegmentedList,
SegmentedItem,
+ SegmentedItemLabel,
SegmentedControlRadio,
+ ItemIcon,
} from "./SegmentedControl.styled";
const optionShape = PropTypes.shape({
- name: PropTypes.node.isRequired,
+ name: PropTypes.node,
value: PropTypes.any.isRequired,
icon: PropTypes.string,
@@ -23,7 +24,9 @@ const propTypes = {
name: PropTypes.string,
value: PropTypes.any,
options: PropTypes.arrayOf(optionShape).isRequired,
+ inactiveColor: PropTypes.string,
onChange: PropTypes.func,
+ fullWidth: PropTypes.bool,
};
export function SegmentedControl({
@@ -31,28 +34,38 @@ export function SegmentedControl({
value,
options,
onChange,
+ fullWidth = false,
+ inactiveColor = "text-medium",
...props
}) {
const id = useMemo(() => _.uniqueId("radio-"), []);
const name = nameFromProps || id;
return (
-
+
{options.map((option, index) => {
const isSelected = option.value === value;
const isFirst = index === 0;
const isLast = index === options.length - 1;
const id = `${name}-${option.value}`;
const labelId = `${name}-${option.value}`;
+ const iconOnly = !option.name;
return (
-
-
+
- {option.icon && }
+ {option.icon && (
+
+ )}
{option.name}
-
-
+
+
);
})}
diff --git a/frontend/src/metabase/components/SegmentedControl.styled.js b/frontend/src/metabase/components/SegmentedControl.styled.js
index 0728869dcb0a..b7a7ab910ae1 100644
--- a/frontend/src/metabase/components/SegmentedControl.styled.js
+++ b/frontend/src/metabase/components/SegmentedControl.styled.js
@@ -1,20 +1,19 @@
+import React from "react";
import styled from "styled-components";
+import _ from "underscore";
+import Icon from "metabase/components/Icon";
import { color } from "metabase/lib/colors";
const BORDER_RADIUS = "8px";
export const SegmentedList = styled.ul`
display: flex;
+ width: ${props => (props.fullWidth ? 1 : 0)};
`;
-export const SegmentedItem = styled.label`
- position: relative;
+export const SegmentedItem = styled.li`
display: flex;
- align-items: center;
- font-weight: bold;
- cursor: pointer;
- color: ${props => (props.isSelected ? color(props.selectedColor) : null)};
- padding: 6px 12px;
+ flex-grow: ${props => (props.fullWidth ? 1 : 0)};
border: 1px solid ${color("border")};
border-right-width: ${props => (props.isLast ? "1px" : 0)};
@@ -22,6 +21,19 @@ export const SegmentedItem = styled.label`
border-bottom-left-radius: ${props => (props.isFirst ? BORDER_RADIUS : 0)};
border-top-right-radius: ${props => (props.isLast ? BORDER_RADIUS : 0)};
border-bottom-right-radius: ${props => (props.isLast ? BORDER_RADIUS : 0)};
+`;
+
+export const SegmentedItemLabel = styled.label`
+ display: flex;
+ width: 100%;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+ font-weight: bold;
+ color: ${props =>
+ props.isSelected ? color(props.selectedColor) : color(props.inactiveColor)};
+ padding: ${props => (props.compact ? "8px" : "8px 12px")};
+ cursor: pointer;
:hover {
color: ${props => (!props.isSelected ? color(props.selectedColor) : null)};
@@ -40,3 +52,11 @@ export const SegmentedControlRadio = styled.input.attrs({ type: "radio" })`
padding: 0;
z-index: 1;
`;
+
+function IconWrapper(props) {
+ return ;
+}
+
+export const ItemIcon = styled(IconWrapper)`
+ margin-right: ${props => (props.iconOnly ? 0 : "4px")};
+`;
diff --git a/frontend/src/metabase/sharing/components/EmailAttachmentPicker.jsx b/frontend/src/metabase/sharing/components/EmailAttachmentPicker.jsx
index 281e644a36d6..d764c886b244 100644
--- a/frontend/src/metabase/sharing/components/EmailAttachmentPicker.jsx
+++ b/frontend/src/metabase/sharing/components/EmailAttachmentPicker.jsx
@@ -4,7 +4,7 @@ import PropTypes from "prop-types";
import _ from "underscore";
import { t } from "ttag";
-import ButtonGroup from "metabase/components/ButtonGroup";
+import { SegmentedControl } from "metabase/components/SegmentedControl";
import CheckBox from "metabase/components/CheckBox";
import Label from "metabase/components/type/Label";
import StackedCheckBox from "metabase/components/StackedCheckBox";
@@ -179,13 +179,14 @@ export default class EmailAttachmentPicker extends Component {
-
diff --git a/frontend/src/metabase/visualizations/components/settings/ChartNestedSettingSeries.jsx b/frontend/src/metabase/visualizations/components/settings/ChartNestedSettingSeries.jsx
index b25478038077..92231c63f938 100644
--- a/frontend/src/metabase/visualizations/components/settings/ChartNestedSettingSeries.jsx
+++ b/frontend/src/metabase/visualizations/components/settings/ChartNestedSettingSeries.jsx
@@ -1,7 +1,7 @@
import React from "react";
import ColorPicker from "metabase/components/ColorPicker";
-import ButtonGroup from "metabase/components/ButtonGroup";
+import { SegmentedControl } from "metabase/components/SegmentedControl";
import Icon from "metabase/components/Icon";
import IconWrapper from "metabase/components/IconWrapper";
@@ -59,15 +59,18 @@ export default class ChartNestedSettingSeries extends React.Component {
}
/>
{isLineAreaBar && !isStacked ? (
- o}
- optionNameFn={o => }
+ options={[
+ { value: "line", icon: "line" },
+ { value: "area", icon: "area" },
+ { value: "bar", icon: "bar" },
+ ]}
onChange={value =>
onChangeObjectSettings(single, { display: value })
}
+ fullWidth
/>
) : null}
{objects.length > 1 ? (
diff --git a/frontend/src/metabase/visualizations/components/settings/ChartSettingButtonGroup.jsx b/frontend/src/metabase/visualizations/components/settings/ChartSettingButtonGroup.jsx
deleted file mode 100644
index e531f342738b..000000000000
--- a/frontend/src/metabase/visualizations/components/settings/ChartSettingButtonGroup.jsx
+++ /dev/null
@@ -1,17 +0,0 @@
-/* eslint-disable react/prop-types */
-import React from "react";
-
-import Icon from "metabase/components/Icon";
-import ButtonGroup from "metabase/components/ButtonGroup";
-
-const ChartSettingButtonGroup = ({ value, onChange, options, ...props }) => (
- (o.icon ? : o.name)}
- />
-);
-
-export default ChartSettingButtonGroup;
diff --git a/frontend/src/metabase/visualizations/components/settings/ChartSettingSegmentedControl.jsx b/frontend/src/metabase/visualizations/components/settings/ChartSettingSegmentedControl.jsx
new file mode 100644
index 000000000000..a0edb332b921
--- /dev/null
+++ b/frontend/src/metabase/visualizations/components/settings/ChartSettingSegmentedControl.jsx
@@ -0,0 +1,8 @@
+import React from "react";
+import { SegmentedControl } from "metabase/components/SegmentedControl";
+
+function ChartSettingSegmentedControl(props) {
+ return ;
+}
+
+export default ChartSettingSegmentedControl;
diff --git a/frontend/src/metabase/visualizations/lib/settings.js b/frontend/src/metabase/visualizations/lib/settings.js
index c822b829e4e7..d2217084fd9e 100644
--- a/frontend/src/metabase/visualizations/lib/settings.js
+++ b/frontend/src/metabase/visualizations/lib/settings.js
@@ -8,7 +8,7 @@ import ChartSettingInputNumeric from "metabase/visualizations/components/setting
import ChartSettingRadio from "metabase/visualizations/components/settings/ChartSettingRadio";
import ChartSettingSelect from "metabase/visualizations/components/settings/ChartSettingSelect";
import ChartSettingToggle from "metabase/visualizations/components/settings/ChartSettingToggle";
-import ChartSettingButtonGroup from "metabase/visualizations/components/settings/ChartSettingButtonGroup";
+import ChartSettingSegmentedControl from "metabase/visualizations/components/settings/ChartSettingSegmentedControl";
import ChartSettingFieldPicker from "metabase/visualizations/components/settings/ChartSettingFieldPicker";
import ChartSettingFieldsPicker from "metabase/visualizations/components/settings/ChartSettingFieldsPicker";
import ChartSettingFieldsPartition from "metabase/visualizations/components/settings/ChartSettingFieldsPartition";
@@ -70,7 +70,7 @@ const WIDGETS = {
radio: ChartSettingRadio,
select: ChartSettingSelect,
toggle: ChartSettingToggle,
- buttonGroup: ChartSettingButtonGroup,
+ segmentedControl: ChartSettingSegmentedControl,
field: ChartSettingFieldPicker,
fields: ChartSettingFieldsPicker,
fieldsPartition: ChartSettingFieldsPartition,
diff --git a/frontend/src/metabase/visualizations/lib/settings/graph.js b/frontend/src/metabase/visualizations/lib/settings/graph.js
index 2c2d3a30e345..eda9c9d6fd63 100644
--- a/frontend/src/metabase/visualizations/lib/settings/graph.js
+++ b/frontend/src/metabase/visualizations/lib/settings/graph.js
@@ -250,12 +250,9 @@ export const STACKABLE_SETTINGS = {
"stackable.stack_display": {
section: t`Display`,
title: t`Stacked chart type`,
- widget: "buttonGroup",
+ widget: "segmentedControl",
props: {
- options: [
- { icon: "area", name: t`Area`, value: "area" },
- { icon: "bar", name: t`Bar`, value: "bar" },
- ],
+ options: [{ icon: "area", value: "area" }, { icon: "bar", value: "bar" }],
},
getDefault: (series, settings) => {
const displays = series.map(single => settings.series(single).display);
diff --git a/frontend/src/metabase/visualizations/lib/settings/series.js b/frontend/src/metabase/visualizations/lib/settings/series.js
index ce535db8279e..6d34d83718b8 100644
--- a/frontend/src/metabase/visualizations/lib/settings/series.js
+++ b/frontend/src/metabase/visualizations/lib/settings/series.js
@@ -63,12 +63,12 @@ export function seriesSetting({
},
"line.interpolate": {
title: t`Line style`,
- widget: "buttonGroup",
+ widget: "segmentedControl",
props: {
options: [
- { icon: "straight", name: t`Line`, value: "linear" },
- { icon: "curved", name: t`Curve`, value: "cardinal" },
- { icon: "stepped", name: t`Step`, value: "step-after" },
+ { icon: "straight", value: "linear" },
+ { icon: "curved", value: "cardinal" },
+ { icon: "stepped", value: "step-after" },
],
},
getHidden: (single, settings) =>
@@ -80,7 +80,7 @@ export function seriesSetting({
},
"line.marker_enabled": {
title: t`Show dots on lines`,
- widget: "buttonGroup",
+ widget: "segmentedControl",
props: {
options: [
{ name: t`Auto`, value: null },
@@ -116,7 +116,7 @@ export function seriesSetting({
},
axis: {
title: t`Which axis?`,
- widget: "buttonGroup",
+ widget: "segmentedControl",
default: null,
props: {
options: [
From fb0eab4d3fb2b62c6af8750dbf29750b4112be0f Mon Sep 17 00:00:00 2001
From: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Date: Mon, 19 Jul 2021 20:49:52 +0200
Subject: [PATCH 058/664] [Cypress runner] Run Cypress (#17099)
Related issue: https://github.com/metabase/metabase/issues/17005
* Add `arg` library
* Rewrite the main Cypress runner logic
* Destructure Cypress snapshot results
* Rename Cypress snapshots runner
* Update CircleCI config to reflect `folder` changes
* Update readme for smoke tests
* Run `metabase-db` tests conditionally based on env `QA_DB_ENABLED`
---
.circleci/config.yml | 37 ++++---
...s => cypress-runner-generate-snapshots.js} | 14 +--
.../__runner__/cypress-runner-run-tests.js | 97 +++++++++++++++++++
frontend/test/__runner__/run_cypress_tests.js | 77 +--------------
.../test/__support__/e2e/cypress-plugins.js | 5 +
frontend/test/metabase-db/README.md | 46 +++++++++
.../metabase/scenarios/smoketest/README.md | 4 +-
package.json | 3 +-
yarn.lock | 5 +
9 files changed, 190 insertions(+), 98 deletions(-)
rename frontend/test/__runner__/{generate-cypress-snapshots.js => cypress-runner-generate-snapshots.js} (68%)
create mode 100644 frontend/test/__runner__/cypress-runner-run-tests.js
create mode 100644 frontend/test/metabase-db/README.md
diff --git a/.circleci/config.yml b/.circleci/config.yml
index c22318d6104d..0aa2bff3f96a 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -883,6 +883,9 @@ jobs:
test-files:
type: string
default: ""
+ qa-db:
+ type: boolean
+ default: false
before-steps:
type: steps
default: []
@@ -891,6 +894,7 @@ jobs:
environment:
MB_EDITION: << parameters.edition >>
CYPRESS_GROUP: << parameters.cypress-group >>
+ QA_DB_ENABLED: << parameters.qa-db >>
DISPLAY: ""
steps:
- attach-workspace
@@ -1177,7 +1181,7 @@ workflows:
requires:
- build-uberjar-<< matrix.edition >>
cypress-group: "smoketest-<< matrix.edition >>"
- source-folder: frontend/test/metabase/scenarios/smoketest
+ source-folder: smoketest
- fe-tests-cypress:
matrix:
@@ -1187,7 +1191,7 @@ workflows:
requires:
- build-uberjar-<< matrix.edition >>
cypress-group: "admin-<< matrix.edition >>"
- source-folder: frontend/test/metabase/scenarios/admin
+ source-folder: admin
- fe-tests-cypress:
matrix:
@@ -1197,7 +1201,7 @@ workflows:
requires:
- build-uberjar-<< matrix.edition >>
cypress-group: "collections-<< matrix.edition >>"
- source-folder: frontend/test/metabase/scenarios/collections
+ source-folder: collections
- fe-tests-cypress:
matrix:
@@ -1207,7 +1211,7 @@ workflows:
requires:
- build-uberjar-<< matrix.edition >>
cypress-group: "dashboard-<< matrix.edition >>"
- source-folder: frontend/test/metabase/scenarios/dashboard
+ source-folder: dashboard
- fe-tests-cypress:
matrix:
@@ -1217,7 +1221,7 @@ workflows:
requires:
- build-uberjar-<< matrix.edition >>
cypress-group: "dashboard-filters-<< matrix.edition >>"
- source-folder: frontend/test/metabase/scenarios/dashboard-filters
+ source-folder: dashboard-filters
- fe-tests-cypress:
matrix:
@@ -1227,7 +1231,7 @@ workflows:
requires:
- build-uberjar-<< matrix.edition >>
cypress-group: "onboarding-<< matrix.edition >>"
- source-folder: frontend/test/metabase/scenarios/onboarding
+ source-folder: onboarding
- fe-tests-cypress:
matrix:
@@ -1237,7 +1241,7 @@ workflows:
requires:
- build-uberjar-<< matrix.edition >>
cypress-group: "native-<< matrix.edition >>"
- source-folder: frontend/test/metabase/scenarios/native
+ source-folder: native
- fe-tests-cypress:
matrix:
@@ -1247,7 +1251,7 @@ workflows:
requires:
- build-uberjar-<< matrix.edition >>
cypress-group: "native-filters-<< matrix.edition >>"
- source-folder: frontend/test/metabase/scenarios/native-filters
+ source-folder: native-filters
- fe-tests-cypress:
matrix:
@@ -1257,7 +1261,7 @@ workflows:
requires:
- build-uberjar-<< matrix.edition >>
cypress-group: "question-<< matrix.edition >>"
- source-folder: frontend/test/metabase/scenarios/question
+ source-folder: question
- fe-tests-cypress:
matrix:
@@ -1267,7 +1271,7 @@ workflows:
requires:
- build-uberjar-<< matrix.edition >>
cypress-group: "binning-<< matrix.edition >>"
- source-folder: frontend/test/metabase/scenarios/binning
+ source-folder: binning
- fe-tests-cypress:
matrix:
@@ -1277,7 +1281,7 @@ workflows:
requires:
- build-uberjar-<< matrix.edition >>
cypress-group: "sharing-<< matrix.edition >>"
- source-folder: frontend/test/metabase/scenarios/sharing
+ source-folder: sharing
- fe-tests-cypress:
matrix:
@@ -1287,7 +1291,7 @@ workflows:
requires:
- build-uberjar-<< matrix.edition >>
cypress-group: "visualizations-<< matrix.edition >>"
- source-folder: frontend/test/metabase/scenarios/visualizations
+ source-folder: visualizations
- fe-tests-cypress:
name: e2e-tests-mongo-4-<< matrix.edition >>
@@ -1295,7 +1299,8 @@ workflows:
- build-uberjar-<< matrix.edition >>
e: fe-mongo-4
cypress-group: "mongo"
- source-folder: frontend/test/metabase-db/mongo
+ source-folder: mongo
+ qa-db: true
before-steps:
- wait-for-port:
port: 27017
@@ -1307,7 +1312,8 @@ workflows:
- build-uberjar-<< matrix.edition >>
e: fe-postgres-12
cypress-group: "postgres"
- source-folder: frontend/test/metabase-db/postgres
+ source-folder: postgres
+ qa-db: true
before-steps:
- wait-for-port:
port: 5432
@@ -1319,7 +1325,8 @@ workflows:
- build-uberjar-<< matrix.edition >>
e: fe-mysql-8
cypress-group: "mysql"
- source-folder: frontend/test/metabase-db/mysql
+ source-folder: mysql
+ qa-db: true
before-steps:
- wait-for-port:
port: 3306
diff --git a/frontend/test/__runner__/generate-cypress-snapshots.js b/frontend/test/__runner__/cypress-runner-generate-snapshots.js
similarity index 68%
rename from frontend/test/__runner__/generate-cypress-snapshots.js
rename to frontend/test/__runner__/cypress-runner-generate-snapshots.js
index 3da0ea0d0a20..373a707f2d7a 100644
--- a/frontend/test/__runner__/generate-cypress-snapshots.js
+++ b/frontend/test/__runner__/cypress-runner-generate-snapshots.js
@@ -13,22 +13,24 @@ const generateSnapshots = async (baseUrl, exitFunction) => {
const snapshotConfig = getConfig(baseUrl);
try {
- const results = await cypress.run(snapshotConfig);
+ const { status, message, totalFailed, failures } = await cypress.run(
+ snapshotConfig,
+ );
// At least one test failed. We can't continue to the next step.
// Cypress tests rely on snapshots correctly generated at this stage.
- if (results.totalFailed > 0) {
+ if (totalFailed > 0) {
await exitFunction(1);
}
// Something went wrong and Cypress failed to even run tests
- if (results.status === "failed" && results.failures) {
- console.error(results.message);
+ if (status === "failed" && failures) {
+ console.error(message);
- await exitFunction(results.failures);
+ await exitFunction(failures);
}
} catch (e) {
- console.error("Unable to generate snapshots\n", e);
+ console.error("Unable to generate snapshots!\n", e);
await exitFunction(1);
}
diff --git a/frontend/test/__runner__/cypress-runner-run-tests.js b/frontend/test/__runner__/cypress-runner-run-tests.js
new file mode 100644
index 000000000000..55e671a98bab
--- /dev/null
+++ b/frontend/test/__runner__/cypress-runner-run-tests.js
@@ -0,0 +1,97 @@
+const cypress = require("cypress");
+const arg = require("arg");
+
+const args = arg(
+ {
+ "--folder": String, // The name of the folder to run files from
+ "--open": [Boolean], // Run Cypress in open mode or not? Doesn't accept additional arguments
+ },
+ { permissive: true }, // Passes all other flags and args to the Cypress parser
+);
+
+const folder = args["--folder"];
+const isFolder = !!folder;
+
+const supportedDatabases = ["mongo", "mysql", "postgres"];
+const isQaDatabase = supportedDatabases.includes(folder);
+
+const isOpenMode = args["--open"];
+const isCI = process.env["CI"];
+
+const parseArguments = async () => {
+ const cliArgs = args._;
+
+ // cypress.cli.parseArguments requires `cypress run` as the first two arguments
+ if (cliArgs[0] !== "cypress") {
+ cliArgs.unshift("cypress");
+ }
+
+ if (cliArgs[1] !== "run") {
+ cliArgs.splice(1, 0, "run");
+ }
+
+ return await cypress.cli.parseRunArguments(cliArgs);
+};
+
+// This overly complicated logic will not be needed once we merge "db" specs with the rest of the "normal" Cypress files.
+// Alternatively we can use the official `--project` flag to isolate "db" files but that would require a major refactor.
+const getSourceFolder = () => {
+ const defaultPath = `./frontend/test/metabase/scenarios/${folder}/**/*.cy.spec.js`;
+ const QaDatabasePath = `./frontend/test/metabase-db/${folder}/**/*.cy.spec.js`;
+
+ return isQaDatabase ? QaDatabasePath : defaultPath;
+};
+
+const getReporterConfig = isCI => {
+ return isCI
+ ? {
+ reporter: "junit",
+ "reporter-options": "mochaFile=cypress/results/results-[hash].xml",
+ }
+ : null;
+};
+
+const runCypress = async (baseUrl, exitFunction) => {
+ const defaultConfig = {
+ configFile: "frontend/test/__support__/e2e/cypress.json",
+ config: {
+ baseUrl,
+ },
+ spec: isFolder && getSourceFolder(),
+ };
+
+ const reporterConfig = getReporterConfig(isCI);
+
+ const userArgs = await parseArguments();
+
+ const finalConfig = Object.assign(
+ {},
+ defaultConfig,
+ reporterConfig,
+ userArgs,
+ );
+
+ try {
+ const { status, message, totalFailed, failures } = isOpenMode
+ ? await cypress.open(finalConfig)
+ : await cypress.run(finalConfig);
+
+ // At least one test failed
+ if (totalFailed > 0) {
+ await exitFunction(1);
+ }
+
+ // Something went wrong and Cypress failed to even run tests
+ if (status === "failed" && failures) {
+ console.error(message);
+
+ await exitFunction(failures);
+ }
+ } catch (e) {
+ console.error("Failed to run Cypress!\n", e);
+
+ await exitFunction(1);
+ }
+};
+
+module.exports = runCypress;
diff --git a/frontend/test/__runner__/run_cypress_tests.js b/frontend/test/__runner__/run_cypress_tests.js
index f59a0296f380..a16b2cc7c43a 100644
--- a/frontend/test/__runner__/run_cypress_tests.js
+++ b/frontend/test/__runner__/run_cypress_tests.js
@@ -1,8 +1,7 @@
-import { spawn } from "child_process";
-
+const { printBold } = require("./cypress-runner-utils");
+const runCypress = require("./cypress-runner-run-tests");
const getVersion = require("./cypress-runner-get-version");
-const generateSnapshots = require("./generate-cypress-snapshots");
-const { printBold, printYellow } = require("./cypress-runner-utils");
+const generateSnapshots = require("./cypress-runner-generate-snapshots");
// Use require for BackendResource to run it after the mock afterAll has been set
const BackendResource = require("./backend.js").BackendResource;
@@ -10,19 +9,6 @@ const BackendResource = require("./backend.js").BackendResource;
const server = BackendResource.get({ dbKey: "" });
const baseUrl = server.host;
-// We currently accept three (optional) command line arguments
-// --open - Opens the Cypress test browser
-// --folder - Specifies a different path for the integration folder
-// --spec - Specifies a path to a single test file
-const userArgs = process.argv.slice(2);
-const isOpenMode = userArgs.includes("--open");
-const isFolderFlag = userArgs.includes("--folder");
-const isSpecFlag = userArgs.includes("--spec");
-const sourceFolderLocation = userArgs[userArgs.indexOf("--folder") + 1];
-const specs = userArgs[userArgs.indexOf("--spec") + 1];
-const isSingleSpec = !specs || !specs.match(/,/);
-const testFiles = isSingleSpec ? specs : specs.split(",");
-
const init = async () => {
printBold("Metabase version info");
await getVersion();
@@ -34,62 +20,7 @@ const init = async () => {
await generateSnapshots(baseUrl, cleanup);
printBold("Starting Cypress");
- if (!isOpenMode) {
- printYellow(
- "If you are developing locally, prefer using `yarn test-cypress-open` instead.\n",
- );
- }
-
- const logMessage = isFolderFlag
- ? `Running tests in '${sourceFolderLocation}'`
- : `Running '${testFiles}'`;
-
- printBold(logMessage);
- const baseConfig = { baseUrl: server.host };
- const folderConfig = isFolderFlag && {
- integrationFolder: sourceFolderLocation,
- };
- const specsConfig = isSpecFlag && { testFiles };
- const ignoreConfig =
- // if we're not running specific tests, avoid including db tests
- folderConfig || specsConfig
- ? null
- : { ignoreTestFiles: "**/metabase-db/**" };
-
- const config = {
- ...baseConfig,
- ...folderConfig,
- ...specsConfig,
- ...ignoreConfig,
- };
- // Cypress suggests using JSON.stringified object for more complex configuration objects
- // See: https://docs.cypress.io/guides/references/configuration#Command-Line
- const commandLineConfig = JSON.stringify(config);
-
- const cypressProcess = spawn(
- "yarn",
- [
- "cypress",
- isOpenMode ? "open" : "run",
- "--config-file",
- process.env["CONFIG_FILE"],
- "--config",
- commandLineConfig,
- ...(process.env["CI"]
- ? [
- "--reporter",
- "junit",
- "--reporter-options",
- "mochaFile=cypress/results/results-[hash].xml",
- ]
- : []),
- ],
- { stdio: "inherit" },
- );
-
- return new Promise((resolve, reject) => {
- cypressProcess.on("exit", resolve);
- });
+ await runCypress(baseUrl, cleanup);
};
const cleanup = async (exitCode = 0) => {
diff --git a/frontend/test/__support__/e2e/cypress-plugins.js b/frontend/test/__support__/e2e/cypress-plugins.js
index 9a2ed74cc57a..08147f1b479b 100644
--- a/frontend/test/__support__/e2e/cypress-plugins.js
+++ b/frontend/test/__support__/e2e/cypress-plugins.js
@@ -17,6 +17,8 @@
const hasEnterpriseToken =
process.env["ENTERPRISE_TOKEN"] && process.env["MB_EDITION"] === "ee";
+const isQaDatabase = process.env["QA_DB_ENABLED"];
+
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
const webpack = require("@cypress/webpack-preprocessor");
@@ -54,6 +56,9 @@ module.exports = (on, config) => {
** CONFIG **
********************************************************************/
+ if (!isQaDatabase) {
+ config.ignoreTestFiles = "**/metabase-db/**";
+ }
config.env.HAS_ENTERPRISE_TOKEN = hasEnterpriseToken;
return config;
diff --git a/frontend/test/metabase-db/README.md b/frontend/test/metabase-db/README.md
new file mode 100644
index 000000000000..f201fec9e29f
--- /dev/null
+++ b/frontend/test/metabase-db/README.md
@@ -0,0 +1,46 @@
+# Metabase QA DB Tests
+
+These files ensure that Metabase is working with some of the supported databases.
+
+## Running
+
+From the root of the repository:
+
+- If you already have built Metabase with `./bin/build`, just run this to run all the tests.
+
+```shell
+#Choose one
+yarn run test-cypress-no-build --folder mongo|mysql|postgres
+
+# or run all tests
+yarn run test-cypress-no-build --spec ./frontend/test/metabase-db/**/*.cy.spec.js
+```
+
+- Active development, add `--open`
+
+### Requirements
+
+Prior to running these tests, please make sure tha you have enabled the `QA_DB_ENABLED` env for that session only.
+
+```bash
+QA_DB_ENABLED=true
+```
+
+```fish
+set -x QA_Db_ENABLED true
+```
+
+The list of all supported databases that we currently have Docker images for can be found at [Metabase QA repo](https://github.com/metabase/metabase-qa).
+
+For the convenience, we'll list docker commands here as well:
+
+```shell
+#Mongo 4
+docker run --rm -p 27017:27017 --name meta-mongo-sample metabase/qa-databases:mongo-sample-4.0
+
+#PostgreSQL 12
+docker run --rm -p 5432:5432 --name meta-postgres12-sample metabase/qa-databases:postgres-sample-12
+
+#MySQL 8
+docker run --rm -p 3306:3306 --name meta-mysql8-sample metabase/qa-databases:mysql-sample-8
+```
diff --git a/frontend/test/metabase/scenarios/smoketest/README.md b/frontend/test/metabase/scenarios/smoketest/README.md
index 34bee510df01..f0d19b1fdd7e 100644
--- a/frontend/test/metabase/scenarios/smoketest/README.md
+++ b/frontend/test/metabase/scenarios/smoketest/README.md
@@ -6,12 +6,10 @@ These files exist to ensure that we can always do the things we expect with Meta
From the root of the repository:
-- If you are running tests that include admin_setup, run `python -m smtpd -n -c DebuggingServer localhost:1025`in terminal first for setting up email through your localhost
-
- If you already have built Metabase with `./bin/build`, just run this to run all the tests.
```shell
-yarn run test-cypress-no-build --folder frontend/test/metabase/scenarios/smoketest
+yarn run test-cypress-no-build --folder smoketest
```
- Active development, add `--open`
diff --git a/package.json b/package.json
index ec6777441267..642bc6b463b1 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
},
"dependencies": {
"ace-builds": "^1.4.7",
+ "arg": "^5.0.0",
"chevrotain": "^6.5.0",
"classlist-polyfill": "^1.2.0",
"classnames": "^2.1.3",
@@ -201,7 +202,7 @@
"test-cypress": "yarn build && ./bin/build-for-test && yarn test-cypress-no-build",
"test-cypress-open": "./bin/build-for-test && yarn test-cypress-no-build --open",
"test-cypress-open-no-backend": "E2E_HOST='http://localhost:3000' yarn test-cypress-no-build --open",
- "test-cypress-no-build": "yarn && CONFIG_FILE=frontend/test/__support__/e2e/cypress.json babel-node ./frontend/test/__runner__/run_cypress_tests.js",
+ "test-cypress-no-build": "yarn && babel-node ./frontend/test/__runner__/run_cypress_tests.js",
"prepare": "husky install"
},
"lint-staged": {
diff --git a/yarn.lock b/yarn.lock
index a954a35ba5d4..c138455ef6a7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1518,6 +1518,11 @@ arch@^2.1.2:
resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11"
integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==
+arg@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.0.tgz#a20e2bb5710e82950a516b3f933fee5ed478be90"
+ integrity sha512-4P8Zm2H+BRS+c/xX1LrHw0qKpEhdlZjLCgWy+d78T9vqa2Z2SiD2wMrYuWIAFy5IZUD7nnNXroRttz+0RzlrzQ==
+
argparse@^1.0.7:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
From 02d0adcbf5a47cb07cca32986859da0463672c1b Mon Sep 17 00:00:00 2001
From: Gustavo Saiani
Date: Mon, 19 Jul 2021 16:14:30 -0300
Subject: [PATCH 059/664] Improve Collections Header component (#17098)
---
.../CollectionHeader/CollectionHeader.jsx | 127 ++++++++++++++++++
.../CollectionHeader.styled.js | 30 +++++
.../CollectionHeader.unit.spec.js | 126 +++++++++++++++++
.../collections/components/Header/Header.jsx | 78 -----------
.../components/Header/Header.styled.js | 18 ---
.../containers/CollectionContent.jsx | 2 +-
.../styled-components/theme/constants.js | 1 +
.../metabase/styled-components/theme/index.js | 2 +
.../metabase/styled-components/theme/space.js | 12 ++
.../theme/space.unit.spec.js | 15 +++
.../metabase/collections/util.unit.spec.js | 20 ++-
11 files changed, 333 insertions(+), 98 deletions(-)
create mode 100644 frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.jsx
create mode 100644 frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.styled.js
create mode 100644 frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.unit.spec.js
delete mode 100644 frontend/src/metabase/collections/components/Header/Header.jsx
delete mode 100644 frontend/src/metabase/collections/components/Header/Header.styled.js
create mode 100644 frontend/src/metabase/styled-components/theme/constants.js
create mode 100644 frontend/src/metabase/styled-components/theme/index.js
create mode 100644 frontend/src/metabase/styled-components/theme/space.js
create mode 100644 frontend/src/metabase/styled-components/theme/space.unit.spec.js
diff --git a/frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.jsx b/frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.jsx
new file mode 100644
index 000000000000..efbc9c84494e
--- /dev/null
+++ b/frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.jsx
@@ -0,0 +1,127 @@
+/* eslint-disable react/prop-types */
+import React from "react";
+import { Flex } from "grid-styled";
+import { t } from "ttag";
+
+import * as Urls from "metabase/lib/urls";
+import { isPersonalCollection } from "metabase/collections/utils";
+import Icon, { IconWrapper } from "metabase/components/Icon";
+import Link from "metabase/components/Link";
+import PageHeading from "metabase/components/type/PageHeading";
+import Tooltip from "metabase/components/Tooltip";
+import CollectionEditMenu from "metabase/collections/components/CollectionEditMenu";
+
+import {
+ DescriptionTooltipIcon,
+ ToggleMobileSidebarIcon,
+} from "./CollectionHeader.styled";
+
+function Title({
+ collection: { description, name },
+ handleToggleMobileSidebar,
+}) {
+ return (
+
+
+
+ {name}
+
+
+ {description && (
+
+
+
+ )}
+
+ );
+}
+
+function PermissionsLink({
+ collection,
+ isAdmin,
+ isPersonal,
+ isPersonalCollectionChild,
+}) {
+ const tooltip = t`Edit the permissions for this collection`;
+ const link = `${Urls.collection(collection)}/permissions`;
+
+ const canChangePermissions =
+ isAdmin && !isPersonal && !isPersonalCollectionChild;
+
+ return canChangePermissions ? (
+
+
+
+
+
+
+
+ ) : null;
+}
+
+function EditMenu({
+ collection,
+ hasWritePermission,
+ isAdmin,
+ isPersonal,
+ isRoot,
+}) {
+ const tooltip = t`Edit collection`;
+
+ const canEditCollection = hasWritePermission && !isPersonal;
+
+ return canEditCollection ? (
+
+ ) : null;
+}
+
+function CreateCollectionLink({
+ collection,
+ collectionId,
+ hasWritePermission,
+}) {
+ const tooltip = t`New collection`;
+ const link = Urls.newCollection(collectionId);
+
+ return hasWritePermission ? (
+
+
+
+
+
+
+
+ ) : null;
+}
+
+function Menu(props) {
+ return (
+
+
+
+
+
+ );
+}
+
+export default function CollectionHeader(props) {
+ const { collection } = props;
+ const isPersonal = isPersonalCollection(collection);
+ const hasWritePermission = collection && collection.can_write;
+
+ return (
+
+
+
+
+ );
+}
diff --git a/frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.styled.js b/frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.styled.js
new file mode 100644
index 000000000000..db2c00e3cae9
--- /dev/null
+++ b/frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.styled.js
@@ -0,0 +1,30 @@
+import styled from "styled-components";
+import { color } from "metabase/lib/colors";
+
+import { breakpointMinSmall, space } from "metabase/styled-components/theme";
+
+import Icon from "metabase/components/Icon";
+
+export const ToggleMobileSidebarIcon = styled(Icon).attrs({
+ name: "burger",
+ size: 20,
+})`
+ cursor: pointer;
+ margin: ${space(0)} ${space(2)} 0 ${space(1)};
+
+ ${breakpointMinSmall} {
+ display: none;
+ }
+`;
+
+export const DescriptionTooltipIcon = styled(Icon).attrs({
+ name: "info",
+})`
+ color: ${color("bg-dark")};
+ margin-left: ${space(1)};
+ margin-top: ${space(0)};
+
+ &:hover {
+ color: ${color("brand")};
+ }
+`;
diff --git a/frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.unit.spec.js b/frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.unit.spec.js
new file mode 100644
index 000000000000..5abcfeaf1380
--- /dev/null
+++ b/frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.unit.spec.js
@@ -0,0 +1,126 @@
+import React from "react";
+import "@testing-library/jest-dom/extend-expect";
+import { render, screen } from "@testing-library/react";
+
+import Header from "./CollectionHeader";
+
+const collection = {
+ name: "Name",
+};
+
+it("should display collection name", () => {
+ render();
+
+ screen.getByText(collection.name);
+});
+
+describe("description tooltip", () => {
+ const ariaLabel = "info icon";
+
+ describe("should not be displayed", () => {
+ it("if description is not received", () => {
+ render();
+
+ expect(screen.queryByLabelText(ariaLabel)).not.toBeInTheDocument();
+ });
+ });
+
+ describe("should be displayed", () => {
+ it("if description is received", () => {
+ const description = "description";
+
+ render();
+
+ screen.getByLabelText(ariaLabel);
+ });
+ });
+});
+
+describe("permissions link", () => {
+ const ariaLabel = "lock icon";
+
+ describe("should not be displayed", () => {
+ it("if user is not admin", () => {
+ render();
+
+ expect(screen.queryByLabelText(ariaLabel)).not.toBeInTheDocument();
+ });
+
+ it("for personal collections", () => {
+ render(
+ ,
+ );
+
+ expect(screen.queryByLabelText(ariaLabel)).not.toBeInTheDocument();
+ });
+
+ it("if a collection is a personal collection child", () => {
+ render(
+ ,
+ );
+
+ expect(screen.queryByLabelText(ariaLabel)).not.toBeInTheDocument();
+ });
+ });
+
+ describe("should be displayed", () => {
+ it("if user is admin", () => {
+ render();
+
+ screen.getByLabelText(ariaLabel);
+ });
+ });
+});
+
+describe("link to edit collection", () => {
+ const ariaLabel = "pencil icon";
+
+ describe("should not be displayed", () => {
+ it("when no detail is passed in the collection to determine if user can change collection", () => {
+ render();
+
+ expect(screen.queryByLabelText(ariaLabel)).not.toBeInTheDocument();
+ });
+
+ it("if user is not allowed to change collection", () => {
+ render();
+
+ expect(screen.queryByLabelText(ariaLabel)).not.toBeInTheDocument();
+ });
+ });
+
+ describe("should be displayed", () => {
+ it("if user is allowed to change collection", () => {
+ render();
+
+ screen.getByLabelText(ariaLabel);
+ });
+ });
+});
+
+describe("link to create a new collection", () => {
+ const ariaLabel = "new_folder icon";
+
+ describe("should not be displayed", () => {
+ it("if user is not allowed to change collection", () => {
+ render();
+
+ expect(screen.queryByLabelText(ariaLabel)).not.toBeInTheDocument();
+ });
+ });
+
+ describe("should be displayed", () => {
+ it("if user is allowed to change collection", () => {
+ render();
+
+ screen.getByLabelText(ariaLabel);
+ });
+ });
+});
diff --git a/frontend/src/metabase/collections/components/Header/Header.jsx b/frontend/src/metabase/collections/components/Header/Header.jsx
deleted file mode 100644
index 1e020addbba5..000000000000
--- a/frontend/src/metabase/collections/components/Header/Header.jsx
+++ /dev/null
@@ -1,78 +0,0 @@
-/* eslint-disable react/prop-types */
-import React from "react";
-import { Flex } from "grid-styled";
-import { t } from "ttag";
-
-import * as Urls from "metabase/lib/urls";
-import { color } from "metabase/lib/colors";
-import Icon, { IconWrapper } from "metabase/components/Icon";
-import Link from "metabase/components/Link";
-import PageHeading from "metabase/components/type/PageHeading";
-import Tooltip from "metabase/components/Tooltip";
-import CollectionEditMenu from "metabase/collections/components/CollectionEditMenu";
-
-import { ToggleMobileSidebarIcon } from "./Header.styled";
-
-export default function Header({
- collection,
- isAdmin,
- isRoot,
- isPersonalCollectionChild,
- collectionId,
- handleToggleMobileSidebar,
-}) {
- return (
-
-
-
-
- {collection.name}
-
- {collection.description && (
-
-
-
- )}
-
-
-
- {isAdmin &&
- !collection.personal_owner_id &&
- !isPersonalCollectionChild && (
-
-
-
-
-
-
-
- )}
- {collection &&
- collection.can_write &&
- !collection.personal_owner_id && (
-
- )}
- {collection && collection.can_write && (
-
-
-
-
-
-
-
- )}
-
-
- );
-}
diff --git a/frontend/src/metabase/collections/components/Header/Header.styled.js b/frontend/src/metabase/collections/components/Header/Header.styled.js
deleted file mode 100644
index bfa59d123560..000000000000
--- a/frontend/src/metabase/collections/components/Header/Header.styled.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import styled from "styled-components";
-
-import Icon from "metabase/components/Icon";
-import { breakpointMinSmall } from "metabase/styled-components/theme/media-queries";
-
-export const ToggleMobileSidebarIcon = styled(Icon).attrs({
- ml: 1,
- mr: 2,
- mt: "4px",
- name: "burger",
- size: 20,
-})`
- cursor: pointer;
-
- ${breakpointMinSmall} {
- display: none;
- }
-`;
diff --git a/frontend/src/metabase/collections/containers/CollectionContent.jsx b/frontend/src/metabase/collections/containers/CollectionContent.jsx
index fdb109b6da0b..7950bdbc8d37 100644
--- a/frontend/src/metabase/collections/containers/CollectionContent.jsx
+++ b/frontend/src/metabase/collections/containers/CollectionContent.jsx
@@ -11,7 +11,7 @@ import { getUserIsAdmin } from "metabase/selectors/user";
import BulkActions from "metabase/collections/components/BulkActions";
import CollectionEmptyState from "metabase/components/CollectionEmptyState";
-import Header from "metabase/collections/components/Header/Header";
+import Header from "metabase/collections/components/CollectionHeader/CollectionHeader";
import ItemsTable from "metabase/collections/components/ItemsTable";
import PinnedItemsTable from "metabase/collections/components/PinnedItemsTable";
import { isPersonalCollectionChild } from "metabase/collections/utils";
diff --git a/frontend/src/metabase/styled-components/theme/constants.js b/frontend/src/metabase/styled-components/theme/constants.js
new file mode 100644
index 000000000000..ee08cbe63e7e
--- /dev/null
+++ b/frontend/src/metabase/styled-components/theme/constants.js
@@ -0,0 +1 @@
+export const SPACE_LEVELS = ["4px", "8px", "16px", "32px", "64px", "128px"];
diff --git a/frontend/src/metabase/styled-components/theme/index.js b/frontend/src/metabase/styled-components/theme/index.js
new file mode 100644
index 000000000000..9d0b5ba75805
--- /dev/null
+++ b/frontend/src/metabase/styled-components/theme/index.js
@@ -0,0 +1,2 @@
+export * from "./media-queries";
+export * from "./space";
diff --git a/frontend/src/metabase/styled-components/theme/space.js b/frontend/src/metabase/styled-components/theme/space.js
new file mode 100644
index 000000000000..b7fb5f0df9e1
--- /dev/null
+++ b/frontend/src/metabase/styled-components/theme/space.js
@@ -0,0 +1,12 @@
+import { SPACE_LEVELS as levels } from "./constants";
+
+/**
+ * Returns a pixel amount: 4px, 8px, 16px, on to 128px
+ * @param {number} level must be an integer between 0 and 5
+ * @returns {string}
+ */
+export function space(level = 0) {
+ const spaceInteger = levels[level];
+
+ return spaceInteger || "";
+}
diff --git a/frontend/src/metabase/styled-components/theme/space.unit.spec.js b/frontend/src/metabase/styled-components/theme/space.unit.spec.js
new file mode 100644
index 000000000000..895af8f42c99
--- /dev/null
+++ b/frontend/src/metabase/styled-components/theme/space.unit.spec.js
@@ -0,0 +1,15 @@
+import { space } from "./space";
+
+it("returns pixel amount for acceptable levels", () => {
+ expect(space(0)).toBe("4px");
+ expect(space(1)).toBe("8px");
+ expect(space(2)).toBe("16px");
+ expect(space(3)).toBe("32px");
+ expect(space(4)).toBe("64px");
+ expect(space(5)).toBe("128px");
+});
+
+it("returns empty string for unacceptable integer levels", () => {
+ expect(space(-1)).toBe("");
+ expect(space(6)).toBe("");
+});
diff --git a/frontend/test/metabase/collections/util.unit.spec.js b/frontend/test/metabase/collections/util.unit.spec.js
index 4c61ae9bd085..fabfbc022ddb 100644
--- a/frontend/test/metabase/collections/util.unit.spec.js
+++ b/frontend/test/metabase/collections/util.unit.spec.js
@@ -1,4 +1,22 @@
-import { getParentPath } from "metabase/collections/utils";
+import {
+ getParentPath,
+ isPersonalCollection,
+} from "metabase/collections/utils";
+
+describe("isPersonalCollection", () => {
+ it("returns true if personal_owner_id is a number", () => {
+ const collection = { personal_owner_id: 1 };
+
+ expect(isPersonalCollection(collection)).toBe(true);
+ });
+
+ it("returns false if personal_owner_id is not a number", () => {
+ const collection = {};
+
+ expect(isPersonalCollection(collection)).toBe(false);
+ });
+});
+
describe("getParentPath", () => {
it("should return the proper path to a child object", () => {
const testList = [
From 3927d0c706dba69856b04cf6c2312675a4cd6001 Mon Sep 17 00:00:00 2001
From: Alexander Polyankin
Date: Mon, 19 Jul 2021 23:25:09 +0300
Subject: [PATCH 060/664] Remove Number from semantic types (#17109)
---
frontend/src/metabase/lib/core.js | 5 ---
.../admin/datamodel/field-type.cy.spec.js | 32 +++++++++++--------
2 files changed, 19 insertions(+), 18 deletions(-)
diff --git a/frontend/src/metabase/lib/core.js b/frontend/src/metabase/lib/core.js
index c173bfb83029..9cb3bfcf9b65 100644
--- a/frontend/src/metabase/lib/core.js
+++ b/frontend/src/metabase/lib/core.js
@@ -38,11 +38,6 @@ export const field_semantic_types = [
name: t`Description`,
section: t`Common`,
},
- {
- id: TYPE.Number,
- name: t`Number`,
- section: t`Common`,
- },
{
id: TYPE.Title,
name: t`Title`,
diff --git a/frontend/test/metabase/scenarios/admin/datamodel/field-type.cy.spec.js b/frontend/test/metabase/scenarios/admin/datamodel/field-type.cy.spec.js
index 2d4e0c449f4d..e75604c7fa0a 100644
--- a/frontend/test/metabase/scenarios/admin/datamodel/field-type.cy.spec.js
+++ b/frontend/test/metabase/scenarios/admin/datamodel/field-type.cy.spec.js
@@ -48,16 +48,10 @@ describe("scenarios > admin > datamodel > field > field type", () => {
getFKTargetField("Products → ID");
});
- it.skip("should let you change the type to 'Number' (metabase#16781)", () => {
+ it("should not let you change the type to 'Number' (metabase#16781)", () => {
visitAlias("@ORDERS_PRODUCT_ID_URL");
- setFieldType({ oldValue: "Foreign Key", newValue: "Number" });
-
- waitAndAssertOnResponse("fieldUpdate");
-
- cy.reload();
-
- getFieldType("Number");
+ checkNoFieldType({ oldValue: "Foreign Key", newValue: "Number" });
});
});
@@ -77,15 +71,27 @@ function getFieldType(type) {
function setFieldType({ oldValue, newValue } = {}) {
getFieldType(oldValue).click();
+
+ popover().within(() => {
+ searchFieldType(newValue);
+ cy.findByText(newValue).click();
+ });
+}
+
+function checkNoFieldType({ oldValue, newValue } = {}) {
+ getFieldType(oldValue).click();
+
popover().within(() => {
- cy.get(".ReactVirtualized__Grid").scrollTo(0, 0); // HACK: scroll to the top of the list. Ideally we should probably disable AccordionList virtualization
- cy.findByPlaceholderText("Find...").type(newValue);
- cy.get(".List-item")
- .contains(newValue)
- .click();
+ searchFieldType(newValue);
+ cy.queryByText(newValue).should("not.exist");
});
}
+function searchFieldType(type) {
+ cy.get(".ReactVirtualized__Grid").scrollTo(0, 0); // HACK: scroll to the top of the list. Ideally we should probably disable AccordionList virtualization
+ cy.findByPlaceholderText("Find...").type(type);
+}
+
function getFKTargetField(targetField) {
return cy
.get(".TableEditor-field-target")
From b48e0bd3d73bb044d94012d3515a1cf2f7e064f9 Mon Sep 17 00:00:00 2001
From: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Date: Mon, 19 Jul 2021 23:27:06 +0200
Subject: [PATCH 061/664] #17043 Repro: With Whitelabel custom name, Metabase
name is still shown multiple places in Admin Panel (#17112)
---
.../scenarios/admin/settings/whitelabel.cy.spec.js | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/frontend/test/metabase/scenarios/admin/settings/whitelabel.cy.spec.js b/frontend/test/metabase/scenarios/admin/settings/whitelabel.cy.spec.js
index 78d7a3c8864f..2e72ae6a836b 100644
--- a/frontend/test/metabase/scenarios/admin/settings/whitelabel.cy.spec.js
+++ b/frontend/test/metabase/scenarios/admin/settings/whitelabel.cy.spec.js
@@ -120,6 +120,19 @@ describeWithToken("formatting > whitelabel", () => {
cy.findByText(`${COMPANY_NAME} is up and running.`);
cy.findByText("Metabase is up and running.").should("not.exist");
});
+
+ it.skip("should not show the old name in the admin panel (metabase#17043)", () => {
+ cy.reload();
+
+ cy.findByDisplayValue(COMPANY_NAME);
+ cy.contains(
+ `These are the primary colors used in charts and throughout ${COMPANY_NAME}.`,
+ );
+ cy.contains(`The top nav bar of ${COMPANY_NAME}.`);
+
+ cy.visit("/admin/settings/general");
+ cy.contains(`The name used for this instance of ${COMPANY_NAME}.`);
+ });
});
describe("company color theme", () => {
From 856d25c1091923b1e1d567ed266e84c7b55f9242 Mon Sep 17 00:00:00 2001
From: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Date: Tue, 20 Jul 2021 00:28:29 +0200
Subject: [PATCH 062/664] Run Cypress jobs in CircleCI as a matrix (#17104)
---
.circleci/config.yml | 120 +++----------------------------------------
1 file changed, 7 insertions(+), 113 deletions(-)
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 0aa2bff3f96a..3eb635bfe628 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -880,6 +880,9 @@ jobs:
source-folder:
type: string
default: ""
+ folder:
+ type: string
+ default: ""
test-files:
type: string
default: ""
@@ -1177,121 +1180,12 @@ workflows:
matrix:
parameters:
edition: ["ee", "oss"]
- name: e2e-tests-smoketest-<< matrix.edition >>
- requires:
- - build-uberjar-<< matrix.edition >>
- cypress-group: "smoketest-<< matrix.edition >>"
- source-folder: smoketest
-
- - fe-tests-cypress:
- matrix:
- parameters:
- edition: ["ee", "oss"]
- name: e2e-tests-admin-<< matrix.edition >>
- requires:
- - build-uberjar-<< matrix.edition >>
- cypress-group: "admin-<< matrix.edition >>"
- source-folder: admin
-
- - fe-tests-cypress:
- matrix:
- parameters:
- edition: ["ee", "oss"]
- name: e2e-tests-collections-<< matrix.edition >>
- requires:
- - build-uberjar-<< matrix.edition >>
- cypress-group: "collections-<< matrix.edition >>"
- source-folder: collections
-
- - fe-tests-cypress:
- matrix:
- parameters:
- edition: ["ee", "oss"]
- name: e2e-tests-dashboard-<< matrix.edition >>
- requires:
- - build-uberjar-<< matrix.edition >>
- cypress-group: "dashboard-<< matrix.edition >>"
- source-folder: dashboard
-
- - fe-tests-cypress:
- matrix:
- parameters:
- edition: ["ee", "oss"]
- name: e2e-tests-dashboard-filters-<< matrix.edition >>
- requires:
- - build-uberjar-<< matrix.edition >>
- cypress-group: "dashboard-filters-<< matrix.edition >>"
- source-folder: dashboard-filters
-
- - fe-tests-cypress:
- matrix:
- parameters:
- edition: ["ee", "oss"]
- name: e2e-tests-onboarding-<< matrix.edition >>
- requires:
- - build-uberjar-<< matrix.edition >>
- cypress-group: "onboarding-<< matrix.edition >>"
- source-folder: onboarding
-
- - fe-tests-cypress:
- matrix:
- parameters:
- edition: ["ee", "oss"]
- name: e2e-tests-native-<< matrix.edition >>
- requires:
- - build-uberjar-<< matrix.edition >>
- cypress-group: "native-<< matrix.edition >>"
- source-folder: native
-
- - fe-tests-cypress:
- matrix:
- parameters:
- edition: ["ee", "oss"]
- name: e2e-tests-native-filters-<< matrix.edition >>
- requires:
- - build-uberjar-<< matrix.edition >>
- cypress-group: "native-filters-<< matrix.edition >>"
- source-folder: native-filters
-
- - fe-tests-cypress:
- matrix:
- parameters:
- edition: ["ee", "oss"]
- name: e2e-tests-question-<< matrix.edition >>
- requires:
- - build-uberjar-<< matrix.edition >>
- cypress-group: "question-<< matrix.edition >>"
- source-folder: question
-
- - fe-tests-cypress:
- matrix:
- parameters:
- edition: ["ee", "oss"]
- name: e2e-tests-binning-<< matrix.edition >>
- requires:
- - build-uberjar-<< matrix.edition >>
- cypress-group: "binning-<< matrix.edition >>"
- source-folder: binning
-
- - fe-tests-cypress:
- matrix:
- parameters:
- edition: ["ee", "oss"]
- name: e2e-tests-sharing-<< matrix.edition >>
- requires:
- - build-uberjar-<< matrix.edition >>
- cypress-group: "sharing-<< matrix.edition >>"
- source-folder: sharing
-
- - fe-tests-cypress:
- matrix:
- parameters:
- edition: ["ee", "oss"]
- name: e2e-tests-visualizations-<< matrix.edition >>
+ folder: ["admin", "binning", "collections", "dashboard", "dashboard-filters", "onboarding", "native", "native-filters", "question", "sharing", "smoketest", "visualizations"]
+ name: e2e-tests-<< matrix.folder >>-<< matrix.edition >>
requires:
- build-uberjar-<< matrix.edition >>
- cypress-group: "visualizations-<< matrix.edition >>"
- source-folder: visualizations
+ cypress-group: "<< matrix.folder >>-<< matrix.edition >>"
+ source-folder: << matrix.folder >>
- fe-tests-cypress:
name: e2e-tests-mongo-4-<< matrix.edition >>
From 5512dd1295caed720aedfaa33ad79c311604d631 Mon Sep 17 00:00:00 2001
From: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Date: Tue, 20 Jul 2021 00:47:22 +0200
Subject: [PATCH 063/664] Upgrade Webpack to the v5 (#16113)
* Update Webpack to the latest version (5.37.0)
* Update `webpack-dev-server` to the latest version (3.11.2)
* Install `webpack-cli`
* Replace `CommonsChunkPlugin` with `splitCunks`
* Remove `NamedModulesPlugin`
* Remove `UnusedFilesWebpackPlugin`
* Replace `extract-text-webpack-plugin` with `mini-css-extract-plugin`
* Upgrade `html-webpack-plugin` to the latest version (5.3.1)
* Upgrade `html-webpack-harddisk-plugin` to the latest version (2.0.0)
* Remove `banner-webpack-plugin`
* Replace deprecated `[hash]` in the config
* Add `mode` to the config
* Upgrade `file-loader` to the latest version (6.2.0)
* Don't include polyfills for `path` and `crypto`
* Remove `uglifyjs-webpack-plugin` and use terser instead
* Workaround CLJS_GLOBAL needs top-level `process` object
* yarn add node-polyfill-webpack-plugin
* Use some polyfills
Apparently, the polyfill for crypto is still necessary since the front-end
code isn't migrated yet to the browser's native Crypto API
(https://www.w3.org/TR/WebCryptoAPI/)
See also https://www.npmjs.com/package/node-polyfill-webpack-plugin
* Admin smoke test: fix getting the empty DB path
* Onboarding setup test: fix getting the empty DB path
* Replace file-loader with type: "asset/resource"
See https://webpack.js.org/guides/asset-modules/
* upgrade react-markdown (#16934)
* Use large CI resource since webpack consumes a lot of memory [ci nocache]
https://circleci.com/docs/2.0/configuration-reference/#resourceclass
* Tidy up `NODE_ENV` references
* Remove Webpack `DefinePlugin`
Co-authored-by: Ariya Hidayat
Co-authored-by: Dalton Johnson
Co-authored-by: Alexander Lesnenko
---
.circleci/config.yml | 1 +
frontend/src/metabase/lib/formatting.js | 8 +-
.../visualizations/visualizations/Text.jsx | 10 +-
.../onboarding/setup/setup.cy.spec.js | 7 +-
.../scenarios/smoketest/admin.cy.spec.js | 7 +-
package.json | 21 +-
webpack.config.js | 113 +-
yarn.lock | 2591 +++++++++--------
8 files changed, 1483 insertions(+), 1275 deletions(-)
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 3eb635bfe628..dba1264c59e4 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -806,6 +806,7 @@ jobs:
parameters:
<<: *Params
executor: clojure-and-node
+ resource_class: large
steps:
- attach-workspace
- run-on-change:
diff --git a/frontend/src/metabase/lib/formatting.js b/frontend/src/metabase/lib/formatting.js
index 8223609437ba..8b3f0937e3a9 100644
--- a/frontend/src/metabase/lib/formatting.js
+++ b/frontend/src/metabase/lib/formatting.js
@@ -689,7 +689,7 @@ function formatStringFallback(value: Value, options: FormattingOptions = {}) {
const MARKDOWN_RENDERERS = {
// eslint-disable-next-line react/display-name
- link: ({ href, children }) => (
+ a: ({ href, children }) => (
{children}
),
};
@@ -714,7 +714,11 @@ export function formatValue(value: Value, options: FormattingOptions = {}) {
value: formatted,
raw: value,
});
- return ;
+ return (
+
+ {markdown}
+
+ );
} else {
// FIXME: render and get the innerText?
console.warn(
diff --git a/frontend/src/metabase/visualizations/visualizations/Text.jsx b/frontend/src/metabase/visualizations/visualizations/Text.jsx
index 3ff6b8e7ccf4..e0844278c53d 100644
--- a/frontend/src/metabase/visualizations/visualizations/Text.jsx
+++ b/frontend/src/metabase/visualizations/visualizations/Text.jsx
@@ -112,8 +112,9 @@ export default class Text extends Component {
styles["text-card-markdown"],
getSettingsStyle(settings),
)}
- source={settings.text}
- />
+ >
+ {settings.text}
+
) : (
+ >
+ {settings.text}
+
);
}
diff --git a/frontend/test/metabase/scenarios/onboarding/setup/setup.cy.spec.js b/frontend/test/metabase/scenarios/onboarding/setup/setup.cy.spec.js
index 0d9f5575c6e7..4d6cd303a39d 100644
--- a/frontend/test/metabase/scenarios/onboarding/setup/setup.cy.spec.js
+++ b/frontend/test/metabase/scenarios/onboarding/setup/setup.cy.spec.js
@@ -1,4 +1,3 @@
-import path from "path";
import { restore, popover } from "__support__/e2e/cypress";
// we're testing for one known (en) and one unknown (xx) locale
@@ -129,10 +128,8 @@ describe("scenarios > setup", () => {
.closest("button")
.should("be.disabled");
- const dbPath = path.resolve(
- Cypress.config("fileServerFolder"),
- "frontend/test/__runner__/empty.db",
- );
+ const dbFilename = "frontend/test/__runner__/empty.db";
+ const dbPath = Cypress.config("fileServerFolder") + "/" + dbFilename;
cy.findByLabelText("Connection String").type(`file:${dbPath}`);
cy.findByText("Next")
.closest("button")
diff --git a/frontend/test/metabase/scenarios/smoketest/admin.cy.spec.js b/frontend/test/metabase/scenarios/smoketest/admin.cy.spec.js
index 6649885876c6..e3d8e8fae3ff 100644
--- a/frontend/test/metabase/scenarios/smoketest/admin.cy.spec.js
+++ b/frontend/test/metabase/scenarios/smoketest/admin.cy.spec.js
@@ -1,4 +1,3 @@
-import path from "path";
import { restore, sidebar } from "__support__/e2e/cypress";
import { USERS } from "__support__/e2e/cypress_data";
@@ -52,10 +51,8 @@ describe("metabase-smoketest > admin", () => {
cy.findByText("H2").click();
cy.findByLabelText("Name").type("Metabase H2");
- const dbPath = path.resolve(
- Cypress.config("fileServerFolder"),
- "frontend/test/__runner__/empty.db",
- );
+ const dbFilename = "frontend/test/__runner__/empty.db";
+ const dbPath = Cypress.config("fileServerFolder") + "/" + dbFilename;
cy.findByLabelText("Connection String").type(`file:${dbPath}`);
cy.findByText("Next").click();
diff --git a/package.json b/package.json
index 642bc6b463b1..73e5b4502abf 100644
--- a/package.json
+++ b/package.json
@@ -40,6 +40,7 @@
"moment": "2.19.3",
"moment-timezone": "^0.5.26",
"mustache": "^2.3.2",
+ "node-polyfill-webpack-plugin": "^1.1.2",
"normalizr": "^3.0.2",
"number-to-locale-string": "^1.0.1",
"password-generator": "^2.0.1",
@@ -57,7 +58,7 @@
"react-element-to-jsx-string": "^13.1.0",
"react-grid-layout": "^1.2.5",
"react-hot-loader": "^4.13.0",
- "react-markdown": "^3.6.0",
+ "react-markdown": "^6.0.2",
"react-motion": "^0.4.5",
"react-redux": "^5.0.4",
"react-resizable": "^1.9.0",
@@ -86,6 +87,7 @@
"tether": "^1.2.0",
"ttag": "1.7.15",
"underscore": "^1.8.3",
+ "yarn.lock": "^0.0.1-security",
"z-index": "0.0.1"
},
"devDependencies": {
@@ -113,7 +115,6 @@
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"babel-register": "^6.26.0",
- "banner-webpack-plugin": "^0.2.3",
"concurrently": "^3.1.0",
"css-loader": "^0.28.7",
"cypress": "^6.8.0",
@@ -126,18 +127,17 @@
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-react-hooks": "^4.2.0",
- "extract-text-webpack-plugin": "^3.0.1",
- "file-loader": "^0.11.1",
"fs-promise": "^2.0.2",
"glob": "^7.1.1",
- "html-webpack-harddisk-plugin": "^0.1.0",
- "html-webpack-plugin": "^2.30.1",
+ "html-webpack-harddisk-plugin": "^2.0.0",
+ "html-webpack-plugin": "^5.3.1",
"husky": "^6.0.0",
"image-diff": "^1.6.3",
"jest": "^19.0.2",
"jest-localstorage-mock": "^2.2.0",
"jsonwebtoken": "^7.2.1",
"lint-staged": "^3.3.1",
+ "mini-css-extract-plugin": "^1.6.0",
"mockdate": "^2.0.2",
"mutationobserver-shim": "^0.3.7",
"postcss-cssnext": "^2.4.0",
@@ -149,10 +149,9 @@
"raf": "^3.4.0",
"shadow-cljs": "2.11.20",
"style-loader": "^0.19.0",
- "uglifyjs-webpack-plugin": "^1.0.0",
- "unused-files-webpack-plugin": "^3.0.0",
- "webpack": "^3.8.1",
- "webpack-dev-server": "^2.9.1",
+ "webpack": "^5.37.0",
+ "webpack-cli": "^4.7.0",
+ "webpack-dev-server": "^3.11.2",
"webpack-notifier": "^1.8.0",
"webpack-postcss-tools": "^1.1.2",
"xhr-mock": "^2.4.1",
@@ -175,7 +174,7 @@
"test-timezones": "yarn && ./frontend/test/__runner__/run_timezone_tests",
"build:js": "yarn && webpack --bail",
"build-watch:js": "yarn && webpack --watch",
- "build-hot:js": "yarn && NODE_ENV=hot webpack-dev-server --progress --host 0.0.0.0",
+ "build-hot:js": "yarn && NODE_ENV=hot webpack serve --progress --host 0.0.0.0",
"build:cljs": "yarn && rm -rf frontend/src/cljs/* && shadow-cljs release app",
"build-quick:cljs": "yarn build:cljs",
"build-watch:cljs": "yarn cljs-server-restart && shadow-cljs watch app",
diff --git a/webpack.config.js b/webpack.config.js
index 4c5091954977..96a8ec365f6b 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -5,14 +5,14 @@ require("babel-register");
require("core-js/stable");
require("regenerator-runtime/runtime");
+const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
+
const webpack = require("webpack");
-const ExtractTextPlugin = require("extract-text-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const HtmlWebpackHarddiskPlugin = require("html-webpack-harddisk-plugin");
-const UnusedFilesWebpackPlugin = require("unused-files-webpack-plugin").default;
-const BannerWebpackPlugin = require("banner-webpack-plugin");
-const UglifyJSPlugin = require("uglifyjs-webpack-plugin");
+const TerserPlugin = require("terser-webpack-plugin");
const WebpackNotifierPlugin = require("webpack-notifier");
const fs = require("fs");
@@ -29,7 +29,8 @@ const TEST_SUPPORT_PATH = __dirname + "/frontend/test/__support__";
const BUILD_PATH = __dirname + "/resources/frontend_client";
// default NODE_ENV to development
-const NODE_ENV = process.env["NODE_ENV"] || "development";
+const NODE_ENV = process.env.NODE_ENV || "development";
+const devMode = NODE_ENV !== "production";
// Babel:
const BABEL_CONFIG = {
@@ -37,14 +38,14 @@ const BABEL_CONFIG = {
};
const CSS_CONFIG = {
- localIdentName:
- NODE_ENV !== "production"
- ? "[name]__[local]___[hash:base64:5]"
- : "[hash:base64:5]",
+ localIdentName: devMode
+ ? "[name]__[local]___[hash:base64:5]"
+ : "[hash:base64:5]",
importLoaders: 1,
};
const config = (module.exports = {
+ mode: devMode ? "development" : "production",
context: SRC_PATH,
// output a bundle for the app JS and a bundle for styles
@@ -60,7 +61,7 @@ const config = (module.exports = {
output: {
path: BUILD_PATH + "/app/dist",
// NOTE: the filename on disk won't include "?[chunkhash]" but the URL in index.html generated by HtmlWebpackPlugin will:
- filename: "[name].bundle.js?[hash]",
+ filename: "[name].bundle.js?[chunkhash]",
publicPath: "app/dist/",
},
@@ -85,18 +86,22 @@ const config = (module.exports = {
},
{
test: /\.(eot|woff2?|ttf|svg|png)$/,
- use: [{ loader: "file-loader" }],
+ type: "asset/resource",
},
{
test: /\.css$/,
- use: ExtractTextPlugin.extract({
- fallback: "style-loader",
- use: [
- { loader: "css-loader", options: CSS_CONFIG },
- { loader: "postcss-loader" },
- ],
- publicPath: "./",
- }),
+ use: [
+ devMode
+ ? "style-loader"
+ : {
+ loader: MiniCssExtractPlugin.loader,
+ options: {
+ publicPath: "./",
+ },
+ },
+ { loader: "css-loader", options: CSS_CONFIG },
+ { loader: "postcss-loader" },
+ ],
},
],
},
@@ -109,7 +114,7 @@ const config = (module.exports = {
"metabase-lib": LIB_SRC_PATH,
"metabase-enterprise": ENTERPRISE_SRC_PATH,
"metabase-types": TYPES_SRC_PATH,
- "cljs": CLJS_SRC_PATH,
+ cljs: CLJS_SRC_PATH,
__support__: TEST_SUPPORT_PATH,
style: SRC_PATH + "/css/core/index",
ace: __dirname + "/node_modules/ace-builds/src-min-noconflict",
@@ -125,31 +130,18 @@ const config = (module.exports = {
},
},
+ optimization: {
+ splitChunks: {
+ chunks: "all",
+ },
+ },
+
plugins: [
- new webpack.optimize.CommonsChunkPlugin({
- name: "vendor",
- minChunks(module) {
- return module.context && module.context.indexOf("node_modules") >= 0;
- },
- }),
- new UnusedFilesWebpackPlugin({
- globOptions: {
- ignore: [
- "**/types.js",
- "**/types/*.js",
- "**/*.spec.*",
- "**/__support__/*.js",
- "**/__mocks__/*.js*",
- "internal/lib/components-node.js",
- "**/noop.js",
- ],
- },
- }),
// Extracts initial CSS into a standard stylesheet that can be loaded in parallel with JavaScript
// NOTE: the filename on disk won't include "?[chunkhash]" but the URL in index.html generated by HtmlWebpackPlugin will:
- new ExtractTextPlugin({
- filename: "[name].bundle.css?[contenthash]",
- allChunks: true,
+ new MiniCssExtractPlugin({
+ filename: devMode ? "[name].css" : "[name].[contenthash].css",
+ chunkFilename: devMode ? "[id].css" : "[id].[contenthash].css",
}),
new HtmlWebpackPlugin({
filename: "../../index.html",
@@ -178,32 +170,17 @@ const config = (module.exports = {
new HtmlWebpackHarddiskPlugin({
outputPath: __dirname + "/resources/frontend_client/app/dist",
}),
- new webpack.DefinePlugin({
- "process.env": { NODE_ENV: JSON.stringify(NODE_ENV) },
- INCLUDE_EE_PLUGINS: JSON.stringify(process.env.MB_EDITION === "ee"),
- }),
- new BannerWebpackPlugin({
- chunks: {
- "app-main": {
- beforeContent:
- "/*\n* This file is subject to the terms and conditions defined in\n * file 'LICENSE.txt', which is part of this source code package.\n */\n",
- },
- "app-public": {
- beforeContent:
- "/*\n* This file is subject to the terms and conditions defined in\n * file 'LICENSE.txt', which is part of this source code package.\n */\n",
- },
- "app-embed": {
- beforeContent:
- "/*\n* This file is subject to the terms and conditions defined in\n * file 'LICENSE-EMBEDDING.txt', which is part of this source code package.\n */\n",
- },
- },
+ new webpack.BannerPlugin({
+ banner:
+ "/*\n* This file is subject to the terms and conditions defined in\n * file 'LICENSE.txt', which is part of this source code package.\n */\n",
}),
+ new NodePolyfillPlugin(), // for crypto, among others
],
});
if (NODE_ENV === "hot") {
// suffixing with ".hot" allows us to run both `yarn run build-hot` and `yarn run test` or `yarn run test-watch` simultaneously
- config.output.filename = "[name].hot.bundle.js?[hash]";
+ config.output.filename = "[name].hot.bundle.js?[contenthash]";
// point the publicPath (inlined in index.html by HtmlWebpackPlugin) to the hot-reloading server
config.output.publicPath =
@@ -222,13 +199,6 @@ if (NODE_ENV === "hot") {
],
});
- // disable ExtractTextPlugin
- config.module.rules[config.module.rules.length - 1].use = [
- { loader: "style-loader" },
- { loader: "css-loader", options: CSS_CONFIG },
- { loader: "postcss-loader" },
- ];
-
config.devServer = {
hot: true,
inline: true,
@@ -262,7 +232,6 @@ if (NODE_ENV === "hot") {
config.plugins.unshift(
new webpack.NoEmitOnErrorsPlugin(),
- new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
);
}
@@ -294,7 +263,9 @@ if (NODE_ENV !== "production") {
}),
);
} else {
- config.plugins.push(new UglifyJSPlugin({ parallel: true, test: /\.jsx?($|\?)/i }));
+ config.plugins.push(
+ new TerserPlugin({ parallel: true, test: /\.jsx?($|\?)/i }),
+ );
config.devtool = "source-map";
}
diff --git a/yarn.lock b/yarn.lock
index c138455ef6a7..23b2a7b65a03 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -966,6 +966,11 @@
debug "^3.1.0"
lodash.once "^4.1.1"
+"@discoveryjs/json-ext@^0.5.0":
+ version "0.5.3"
+ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz#90420f9f9c6d3987f176a19a7d8e764271a2f55d"
+ integrity sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==
+
"@eslint/eslintrc@^0.2.1":
version "0.2.1"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.2.1.tgz#f72069c330461a06684d119384435e12a5d76e3c"
@@ -1143,6 +1148,47 @@
resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.0.tgz#14264692a9d6e2fa4db3df5e56e94b5e25647ac0"
integrity sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A==
+"@types/eslint-scope@^3.7.0":
+ version "3.7.0"
+ resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.0.tgz#4792816e31119ebd506902a482caec4951fabd86"
+ integrity sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==
+ dependencies:
+ "@types/eslint" "*"
+ "@types/estree" "*"
+
+"@types/eslint@*":
+ version "7.2.10"
+ resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.10.tgz#4b7a9368d46c0f8cd5408c23288a59aa2394d917"
+ integrity sha512-kUEPnMKrqbtpCq/KTaGFFKAcz6Ethm2EjCoKIDaCmfRBWLbFuTcOJfTlorwbnboXBzahqWLgUp1BQeKHiJzPUQ==
+ dependencies:
+ "@types/estree" "*"
+ "@types/json-schema" "*"
+
+"@types/estree@*", "@types/estree@^0.0.47":
+ version "0.0.47"
+ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.47.tgz#d7a51db20f0650efec24cd04994f523d93172ed4"
+ integrity sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==
+
+"@types/glob@^7.1.1":
+ version "7.1.3"
+ resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
+ integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==
+ dependencies:
+ "@types/minimatch" "*"
+ "@types/node" "*"
+
+"@types/hast@^2.0.0":
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.2.tgz#236201acca9e2695e42f713d7dd4f151dc2982e4"
+ integrity sha512-Op5W7jYgZI7AWKY5wQ0/QNMzQM7dGQPyW1rXKNiymVCy5iTfdPuGu4HhYNOM2sIv8gUfIuIdcYlXmAepwaowow==
+ dependencies:
+ "@types/unist" "*"
+
+"@types/html-minifier-terser@^5.0.0":
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#3c9ee980f1a10d6021ae6632ca3e79ca2ec4fb50"
+ integrity sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA==
+
"@types/invariant@^2.2.29":
version "2.2.34"
resolved "https://registry.yarnpkg.com/@types/invariant/-/invariant-2.2.34.tgz#05e4f79f465c2007884374d4795452f995720bbe"
@@ -1175,6 +1221,11 @@
dependencies:
"@types/istanbul-lib-report" "*"
+"@types/json-schema@*", "@types/json-schema@^7.0.6":
+ version "7.0.7"
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
+ integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
+
"@types/json-schema@^7.0.5":
version "7.0.6"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
@@ -1190,6 +1241,18 @@
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.162.tgz#65d78c397e0d883f44afbf1f7ba9867022411470"
integrity sha512-alvcho1kRUnnD1Gcl4J+hK0eencvzq9rmzvFPRmP5rPHx9VVsJj6bKLTATPVf9ktgv4ujzh7T+XWKp+jhuODig==
+"@types/mdast@^3.0.0":
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.4.tgz#8ee6b5200751b6cadb9a043ca39612693ad6cb9e"
+ integrity sha512-gIdhbLDFlspL53xzol2hVzrXAbzt71erJHoOwQZWssjaiouOotf03lNtMmFm9VfFkvnLWccSVjUAZGQ5Kqw+jA==
+ dependencies:
+ "@types/unist" "*"
+
+"@types/minimatch@*":
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21"
+ integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==
+
"@types/node@*":
version "14.11.10"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.10.tgz#8c102aba13bf5253f35146affbf8b26275069bef"
@@ -1242,6 +1305,11 @@
dependencies:
pretty-format "^24.3.0"
+"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3":
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.5.tgz#fdd299f23205c3455af88ce618dd65c14cb73e22"
+ integrity sha512-wnra4Vw9dopnuybR6HBywJ/URYpYrKLoepBTEtgfJup8Ahoi2zJECPP2cwiXp7btTvOT2CULv87aQRA4eZSP6g==
+
"@types/yargs-parser@*":
version "15.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
@@ -1261,6 +1329,154 @@
dependencies:
"@types/yargs-parser" "*"
+"@webassemblyjs/ast@1.11.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.0.tgz#a5aa679efdc9e51707a4207139da57920555961f"
+ integrity sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg==
+ dependencies:
+ "@webassemblyjs/helper-numbers" "1.11.0"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
+
+"@webassemblyjs/floating-point-hex-parser@1.11.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz#34d62052f453cd43101d72eab4966a022587947c"
+ integrity sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA==
+
+"@webassemblyjs/helper-api-error@1.11.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz#aaea8fb3b923f4aaa9b512ff541b013ffb68d2d4"
+ integrity sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w==
+
+"@webassemblyjs/helper-buffer@1.11.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz#d026c25d175e388a7dbda9694e91e743cbe9b642"
+ integrity sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA==
+
+"@webassemblyjs/helper-numbers@1.11.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz#7ab04172d54e312cc6ea4286d7d9fa27c88cd4f9"
+ integrity sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ==
+ dependencies:
+ "@webassemblyjs/floating-point-hex-parser" "1.11.0"
+ "@webassemblyjs/helper-api-error" "1.11.0"
+ "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/helper-wasm-bytecode@1.11.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz#85fdcda4129902fe86f81abf7e7236953ec5a4e1"
+ integrity sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA==
+
+"@webassemblyjs/helper-wasm-section@1.11.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz#9ce2cc89300262509c801b4af113d1ca25c1a75b"
+ integrity sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.0"
+ "@webassemblyjs/helper-buffer" "1.11.0"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
+ "@webassemblyjs/wasm-gen" "1.11.0"
+
+"@webassemblyjs/ieee754@1.11.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz#46975d583f9828f5d094ac210e219441c4e6f5cf"
+ integrity sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA==
+ dependencies:
+ "@xtuc/ieee754" "^1.2.0"
+
+"@webassemblyjs/leb128@1.11.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.0.tgz#f7353de1df38aa201cba9fb88b43f41f75ff403b"
+ integrity sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g==
+ dependencies:
+ "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/utf8@1.11.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.0.tgz#86e48f959cf49e0e5091f069a709b862f5a2cadf"
+ integrity sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw==
+
+"@webassemblyjs/wasm-edit@1.11.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz#ee4a5c9f677046a210542ae63897094c2027cb78"
+ integrity sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.0"
+ "@webassemblyjs/helper-buffer" "1.11.0"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
+ "@webassemblyjs/helper-wasm-section" "1.11.0"
+ "@webassemblyjs/wasm-gen" "1.11.0"
+ "@webassemblyjs/wasm-opt" "1.11.0"
+ "@webassemblyjs/wasm-parser" "1.11.0"
+ "@webassemblyjs/wast-printer" "1.11.0"
+
+"@webassemblyjs/wasm-gen@1.11.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz#3cdb35e70082d42a35166988dda64f24ceb97abe"
+ integrity sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.0"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
+ "@webassemblyjs/ieee754" "1.11.0"
+ "@webassemblyjs/leb128" "1.11.0"
+ "@webassemblyjs/utf8" "1.11.0"
+
+"@webassemblyjs/wasm-opt@1.11.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz#1638ae188137f4bb031f568a413cd24d32f92978"
+ integrity sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.0"
+ "@webassemblyjs/helper-buffer" "1.11.0"
+ "@webassemblyjs/wasm-gen" "1.11.0"
+ "@webassemblyjs/wasm-parser" "1.11.0"
+
+"@webassemblyjs/wasm-parser@1.11.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz#3e680b8830d5b13d1ec86cc42f38f3d4a7700754"
+ integrity sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.0"
+ "@webassemblyjs/helper-api-error" "1.11.0"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
+ "@webassemblyjs/ieee754" "1.11.0"
+ "@webassemblyjs/leb128" "1.11.0"
+ "@webassemblyjs/utf8" "1.11.0"
+
+"@webassemblyjs/wast-printer@1.11.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz#680d1f6a5365d6d401974a8e949e05474e1fab7e"
+ integrity sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.0"
+ "@xtuc/long" "4.2.2"
+
+"@webpack-cli/configtest@^1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.0.3.tgz#204bcff87cda3ea4810881f7ea96e5f5321b87b9"
+ integrity sha512-WQs0ep98FXX2XBAfQpRbY0Ma6ADw8JR6xoIkaIiJIzClGOMqVRvPCWqndTxf28DgFopWan0EKtHtg/5W1h0Zkw==
+
+"@webpack-cli/info@^1.2.4":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.2.4.tgz#7381fd41c9577b2d8f6c2594fad397ef49ad5573"
+ integrity sha512-ogE2T4+pLhTTPS/8MM3IjHn0IYplKM4HbVNMCWA9N4NrdPzunwenpCsqKEXyejMfRu6K8mhauIPYf8ZxWG5O6g==
+ dependencies:
+ envinfo "^7.7.3"
+
+"@webpack-cli/serve@^1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.4.0.tgz#f84fd07bcacefe56ce762925798871092f0f228e"
+ integrity sha512-xgT/HqJ+uLWGX+Mzufusl3cgjAcnqYYskaB7o0vRcwOEfuu6hMzSILQpnIzFMGsTaeaX4Nnekl+6fadLbl1/Vg==
+
+"@xtuc/ieee754@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
+ integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==
+
+"@xtuc/long@4.2.2":
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
+ integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
+
JSONStream@^1.0.3:
version "1.3.5"
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0"
@@ -1287,13 +1503,6 @@ ace-builds@^1.4.7:
resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.4.12.tgz#888efa386e36f4345f40b5233fcc4fe4c588fae7"
integrity sha512-G+chJctFPiiLGvs3+/Mly3apXTcfgE45dT5yp12BcWZ1kUs+gm0qd3/fv4gsz6fVag4mM0moHVpjHDIgph6Psg==
-acorn-dynamic-import@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4"
- integrity sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=
- dependencies:
- acorn "^4.0.3"
-
acorn-globals@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf"
@@ -1306,12 +1515,12 @@ acorn-jsx@^5.2.0:
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b"
integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==
-acorn@^4.0.3, acorn@^4.0.4:
+acorn@^4.0.4:
version "4.0.13"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
integrity sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=
-acorn@^5.0.0, acorn@^5.2.1:
+acorn@^5.2.1:
version "5.7.4"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==
@@ -1321,6 +1530,11 @@ acorn@^7.4.0:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
+acorn@^8.2.1:
+ version "8.2.4"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.2.4.tgz#caba24b08185c3b56e3168e97d15ed17f4d31fd0"
+ integrity sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg==
+
adler-32@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/adler-32/-/adler-32-1.2.0.tgz#6a3e6bf0a63900ba15652808cb15c6813d1a5f25"
@@ -1337,6 +1551,11 @@ agent-base@2:
extend "~3.0.0"
semver "~5.0.1"
+ajv-errors@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d"
+ integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==
+
ajv-keywords@^3.1.0, ajv-keywords@^3.5.2:
version "3.5.2"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
@@ -1362,7 +1581,7 @@ ajv@^5.0.0:
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0"
-ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4:
+ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -1372,15 +1591,6 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
-align-text@^0.1.1, align-text@^0.1.3:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
- integrity sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=
- dependencies:
- kind-of "^3.0.2"
- longest "^1.0.1"
- repeat-string "^1.5.2"
-
alphanum-sort@^1.0.1, alphanum-sort@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
@@ -1391,6 +1601,11 @@ amdefine@>=0.0.4:
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=
+ansi-colors@^3.0.0:
+ version "3.2.4"
+ resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf"
+ integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==
+
ansi-colors@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
@@ -1488,14 +1703,6 @@ anymatch@^2.0.0:
micromatch "^3.1.4"
normalize-path "^2.1.1"
-anymatch@~3.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142"
- integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==
- dependencies:
- normalize-path "^3.0.0"
- picomatch "^2.0.4"
-
app-root-path@^2.0.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a"
@@ -1508,11 +1715,6 @@ append-transform@^0.4.0:
dependencies:
default-require-extensions "^1.0.0"
-aproba@^1.1.1:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
- integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
-
arch@^2.1.2:
version "2.2.0"
resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11"
@@ -1565,11 +1767,6 @@ array-equal@^1.0.0:
resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=
-array-find-index@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
- integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=
-
array-find@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/array-find/-/array-find-1.0.0.tgz#6c8e286d11ed768327f8e62ecee87353ca3e78b8"
@@ -1585,7 +1782,7 @@ array-flatten@^2.1.0:
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099"
integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==
-array-includes@^3.0.3, array-includes@^3.1.1:
+array-includes@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348"
integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==
@@ -1700,6 +1897,16 @@ assert@^1.1.1:
object-assign "^4.1.1"
util "0.10.3"
+assert@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/assert/-/assert-2.0.0.tgz#95fc1c616d48713510680f2eaf2d10dd22e02d32"
+ integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==
+ dependencies:
+ es6-object-assign "^1.1.0"
+ is-nan "^1.2.1"
+ object-is "^1.0.1"
+ util "^0.12.0"
+
assign-symbols@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
@@ -1735,7 +1942,7 @@ async@^1.4.0, async@^1.5.0:
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=
-async@^2.1.2, async@^2.1.4, async@^2.4.1, async@^2.6.2:
+async@^2.1.4, async@^2.6.2:
version "2.6.3"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
@@ -1789,6 +1996,11 @@ autoprefixer@^6.0.2, autoprefixer@^6.3.1:
postcss "^5.2.16"
postcss-value-parser "^3.2.3"
+available-typed-arrays@^1.0.2:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz#9e0ae84ecff20caae6a94a1c3bc39b955649b7a9"
+ integrity sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA==
+
aws-sign2@~0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
@@ -2698,14 +2910,6 @@ babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0, babel-runtim
core-js "^2.4.0"
regenerator-runtime "^0.11.0"
-babel-runtime@^7.0.0-beta.3:
- version "7.0.0-beta.3"
- resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-7.0.0-beta.3.tgz#7c750de5514452c27612172506b49085a4a630f2"
- integrity sha512-jlzZ8RACjt0QGxq+wqsw5bCQE9RcUyWpw987mDY3GYxTpOQT2xoyNoG++oVCHzr/nACLBIprfVBNvv/If1ZYcg==
- dependencies:
- core-js "^2.4.0"
- regenerator-runtime "^0.11.0"
-
babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0, babel-template@^6.3.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02"
@@ -2780,16 +2984,16 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
-banner-webpack-plugin@^0.2.3:
- version "0.2.3"
- resolved "https://registry.yarnpkg.com/banner-webpack-plugin/-/banner-webpack-plugin-0.2.3.tgz#e9dee9d9644ccef1fd970e11d82408aff42290eb"
- integrity sha1-6d7p2WRMzvH9lw4R2CQIr/QikOs=
-
base64-js@^1.0.2:
version "1.3.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
+base64-js@^1.3.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
+ integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
+
base@^0.11.1:
version "0.11.2"
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
@@ -2815,11 +3019,6 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
-big.js@^3.1.3:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e"
- integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==
-
big.js@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
@@ -2830,11 +3029,6 @@ binary-extensions@^1.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==
-binary-extensions@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
- integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==
-
bindings@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
@@ -2852,7 +3046,7 @@ bluebird@3.7.1:
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.1.tgz#df70e302b471d7473489acf26a93d63b53f874de"
integrity sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==
-bluebird@^3.3.3, bluebird@^3.4.7, bluebird@^3.5.1, bluebird@^3.7.2:
+bluebird@^3.3.3, bluebird@^3.7.2:
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
@@ -2905,7 +3099,7 @@ bonjour@^3.5.0:
multicast-dns "^6.0.1"
multicast-dns-service-types "^1.1.0"
-boolbase@~1.0.0:
+boolbase@^1.0.0, boolbase@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
@@ -2950,13 +3144,6 @@ braces@^2.3.1, braces@^2.3.2:
split-string "^3.0.2"
to-regex "^3.0.1"
-braces@~3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
- integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
- dependencies:
- fill-range "^7.0.1"
-
brorand@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
@@ -3048,6 +3235,17 @@ browserslist@^4.12.0, browserslist@^4.8.5:
escalade "^3.1.0"
node-releases "^1.1.61"
+browserslist@^4.14.5:
+ version "4.16.6"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2"
+ integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==
+ dependencies:
+ caniuse-lite "^1.0.30001219"
+ colorette "^1.2.2"
+ electron-to-chromium "^1.3.723"
+ escalade "^3.1.1"
+ node-releases "^1.1.71"
+
bser@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bser/-/bser-1.0.2.tgz#381116970b2a6deea5646dd15dd7278444b56169"
@@ -3109,6 +3307,14 @@ buffer@^5.0.3:
base64-js "^1.0.2"
ieee754 "^1.1.4"
+buffer@^6.0.3:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
+ integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
+ dependencies:
+ base64-js "^1.3.1"
+ ieee754 "^1.2.1"
+
buffered-spawn@~1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffered-spawn/-/buffered-spawn-1.1.2.tgz#21ad9735dfbf6576745be0d74a23ef257bf3c58d"
@@ -3138,25 +3344,6 @@ bytes@3.1.0:
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
-cacache@^10.0.4:
- version "10.0.4"
- resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460"
- integrity sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==
- dependencies:
- bluebird "^3.5.1"
- chownr "^1.0.1"
- glob "^7.1.2"
- graceful-fs "^4.1.11"
- lru-cache "^4.1.1"
- mississippi "^2.0.0"
- mkdirp "^0.5.1"
- move-concurrently "^1.0.1"
- promise-inflight "^1.0.1"
- rimraf "^2.6.2"
- ssri "^5.2.4"
- unique-filename "^1.1.0"
- y18n "^4.0.0"
-
cache-base@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
@@ -3209,28 +3396,15 @@ callsites@^3.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
-camel-case@3.0.x:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
- integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=
- dependencies:
- no-case "^2.2.0"
- upper-case "^1.1.1"
-
-camelcase-keys@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
- integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc=
+camel-case@^4.1.1:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a"
+ integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==
dependencies:
- camelcase "^2.0.0"
- map-obj "^1.0.0"
-
-camelcase@^1.0.2:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39"
- integrity sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=
+ pascal-case "^3.1.2"
+ tslib "^2.0.3"
-camelcase@^2.0.0, camelcase@^2.0.1:
+camelcase@^2.0.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=
@@ -3240,11 +3414,6 @@ camelcase@^3.0.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo=
-camelcase@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
- integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
-
camelcase@^5.0.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
@@ -3275,6 +3444,11 @@ caniuse-lite@^1.0.30001135:
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001148.tgz#dc97c7ed918ab33bf8706ddd5e387287e015d637"
integrity sha512-E66qcd0KMKZHNJQt9hiLZGE3J4zuTqE1OnU53miEVtylFbwOEmeA5OsRu90noZful+XGSQOni1aT2tiqu/9yYw==
+caniuse-lite@^1.0.30001219:
+ version "1.0.30001228"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz#bfdc5942cd3326fa51ee0b42fbef4da9d492a7fa"
+ integrity sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A==
+
caseless@~0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7"
@@ -3290,14 +3464,6 @@ ccount@^1.0.0:
resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.5.tgz#ac82a944905a65ce204eb03023157edf29425c17"
integrity sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw==
-center-align@^0.1.1:
- version "0.1.3"
- resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad"
- integrity sha1-qg0yYptu6XIgBBHL1EYckHvCt60=
- dependencies:
- align-text "^0.1.3"
- lazy-cache "^1.0.3"
-
cfb@^1.1.4:
version "1.2.0"
resolved "https://registry.yarnpkg.com/cfb/-/cfb-1.2.0.tgz#6a4d0872b525ed60349e1ef51fb4b0bf73eca9a8"
@@ -3396,7 +3562,7 @@ chokidar@^1.2.0, chokidar@^1.6.1:
optionalDependencies:
fsevents "^1.0.0"
-chokidar@^2.1.2, chokidar@^2.1.8:
+chokidar@^2.1.8:
version "2.1.8"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==
@@ -3415,25 +3581,10 @@ chokidar@^2.1.2, chokidar@^2.1.8:
optionalDependencies:
fsevents "^1.2.7"
-chokidar@^3.4.1:
- version "3.4.3"
- resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b"
- integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==
- dependencies:
- anymatch "~3.1.1"
- braces "~3.0.2"
- glob-parent "~5.1.0"
- is-binary-path "~2.1.0"
- is-glob "~4.0.1"
- normalize-path "~3.0.0"
- readdirp "~3.5.0"
- optionalDependencies:
- fsevents "~2.1.2"
-
-chownr@^1.0.1:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
- integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
+chrome-trace-event@^1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
+ integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==
ci-info@^1.5.0:
version "1.6.0"
@@ -3485,7 +3636,7 @@ classnames@^2.1.3, classnames@^2.2.5:
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
-clean-css@4.2.x:
+clean-css@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78"
integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==
@@ -3538,15 +3689,6 @@ cli-truncate@^0.2.1:
slice-ansi "0.0.4"
string-width "^1.0.1"
-cliui@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1"
- integrity sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=
- dependencies:
- center-align "^0.1.1"
- right-align "^0.1.1"
- wordwrap "0.0.2"
-
cliui@^3.0.3, cliui@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
@@ -3556,20 +3698,29 @@ cliui@^3.0.3, cliui@^3.2.0:
strip-ansi "^3.0.1"
wrap-ansi "^2.0.0"
-cliui@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
- integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
+cliui@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5"
+ integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==
dependencies:
- string-width "^4.2.0"
- strip-ansi "^6.0.0"
- wrap-ansi "^6.2.0"
+ string-width "^3.1.0"
+ strip-ansi "^5.2.0"
+ wrap-ansi "^5.1.0"
clone-buffer@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58"
integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg=
+clone-deep@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
+ integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
+ dependencies:
+ is-plain-object "^2.0.4"
+ kind-of "^6.0.2"
+ shallow-clone "^3.0.0"
+
clone-stats@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1"
@@ -3718,6 +3869,11 @@ color@^3.0.0:
color-convert "^1.9.1"
color-string "^1.5.4"
+colorette@^1.2.1, colorette@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
+ integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
+
colormin@^1.0.5:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133"
@@ -3749,45 +3905,45 @@ combined-stream@^1.0.5, combined-stream@^1.0.6, combined-stream@~1.0.5, combined
dependencies:
delayed-stream "~1.0.0"
-comma-separated-tokens@^1.0.1:
+comma-separated-tokens@^1.0.0, comma-separated-tokens@^1.0.1:
version "1.0.8"
resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea"
integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==
-commander@2.17.x, commander@~2.17.1:
- version "2.17.1"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
- integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
-
commander@2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.6.0.tgz#9df7e52fb2a0cb0fb89058ee80c3104225f37e1d"
integrity sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0=
-commander@^2.11.0, commander@^2.5.0, commander@^2.9.0:
+commander@^2.11.0, commander@^2.20.0, commander@^2.5.0, commander@^2.9.0:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+commander@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
+ integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
+
commander@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==
-commander@~2.13.0:
- version "2.13.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
- integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==
+commander@^7.0.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
+ integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
commander@~2.14.1:
version "2.14.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa"
integrity sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw==
-commander@~2.19.0:
- version "2.19.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
- integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
+commander@~2.17.1:
+ version "2.17.1"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
+ integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
commander@~2.9.0:
version "2.9.0"
@@ -3833,7 +3989,7 @@ compressible@~2.0.16:
dependencies:
mime-db ">= 1.43.0 < 2"
-compression@^1.7.3:
+compression@^1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==
@@ -3885,12 +4041,12 @@ concurrently@^3.1.0:
supports-color "^3.2.3"
tree-kill "^1.1.0"
-connect-history-api-fallback@^1.3.0:
+connect-history-api-fallback@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc"
integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==
-console-browserify@^1.1.0:
+console-browserify@^1.1.0, console-browserify@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==
@@ -3944,18 +4100,6 @@ cookie@0.4.0:
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
-copy-concurrently@^1.0.0:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0"
- integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==
- dependencies:
- aproba "^1.1.1"
- fs-write-stream-atomic "^1.0.8"
- iferr "^0.1.5"
- mkdirp "^0.5.1"
- rimraf "^2.5.4"
- run-queue "^1.0.0"
-
copy-descriptor@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
@@ -4100,7 +4244,18 @@ cross-spawn@^5.0.1:
shebang-command "^1.2.0"
which "^1.2.9"
-cross-spawn@^7.0.0, cross-spawn@^7.0.2:
+cross-spawn@^6.0.0:
+ version "6.0.5"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
+ integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
+ dependencies:
+ nice-try "^1.0.4"
+ path-key "^2.0.1"
+ semver "^5.5.0"
+ shebang-command "^1.2.0"
+ which "^1.2.9"
+
+cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
@@ -4126,7 +4281,7 @@ cryptiles@2.x.x:
dependencies:
boom "2.x.x"
-crypto-browserify@^3.11.0:
+crypto-browserify@^3.11.0, crypto-browserify@^3.12.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==
@@ -4183,15 +4338,15 @@ css-loader@^0.28.7:
postcss-value-parser "^3.3.0"
source-list-map "^2.0.0"
-css-select@^1.1.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
- integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=
+css-select@^2.0.2:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef"
+ integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==
dependencies:
- boolbase "~1.0.0"
- css-what "2.1"
- domutils "1.5.1"
- nth-check "~1.0.1"
+ boolbase "^1.0.0"
+ css-what "^3.2.1"
+ domutils "^1.7.0"
+ nth-check "^1.0.2"
css-selector-tokenizer@^0.7.0:
version "0.7.3"
@@ -4210,10 +4365,10 @@ css-to-react-native@^2.0.3:
css-color-keywords "^1.0.0"
postcss-value-parser "^3.3.0"
-css-what@2.1:
- version "2.1.3"
- resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
- integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
+css-what@^3.2.1:
+ version "3.4.2"
+ resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4"
+ integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==
css.escape@^1.5.1:
version "1.5.1"
@@ -4303,23 +4458,11 @@ cuint@^0.2.2:
resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b"
integrity sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=
-currently-unhandled@^0.4.1:
- version "0.4.1"
- resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
- integrity sha1-mI3zP+qxke95mmE2nddsF635V+o=
- dependencies:
- array-find-index "^1.0.1"
-
cycle@1.0.x:
version "1.0.3"
resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2"
integrity sha1-IegLK+hYD5i0aPN5QwZisEbDStI=
-cyclist@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
- integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
-
cypress-real-events@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/cypress-real-events/-/cypress-real-events-1.4.0.tgz#39575031a4020581e0bbf105d7a306ee57d94f48"
@@ -4427,14 +4570,6 @@ d3@^3, d3@^3.5.17:
resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8"
integrity sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g=
-d@1, d@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
- integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==
- dependencies:
- es5-ext "^0.10.50"
- type "^1.0.1"
-
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
@@ -4460,7 +4595,7 @@ dc@2.1.9:
crossfilter2 "~1.3"
d3 "^3"
-debug@2, debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9:
+debug@2, debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
@@ -4474,7 +4609,7 @@ debug@4.1.1:
dependencies:
ms "^2.1.1"
-debug@4.3.2:
+debug@4.3.2, debug@^4.0.0:
version "4.3.2"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
@@ -4488,6 +4623,13 @@ debug@^3.1.0, debug@^3.1.1:
dependencies:
ms "^2.1.1"
+debug@^3.2.6:
+ version "3.2.7"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
+ integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
+ dependencies:
+ ms "^2.1.1"
+
debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
version "4.2.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"
@@ -4502,7 +4644,7 @@ debug@~2.2.0:
dependencies:
ms "0.7.1"
-decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0:
+decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
@@ -4544,6 +4686,14 @@ deep-is@^0.1.3, deep-is@~0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
+default-gateway@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b"
+ integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==
+ dependencies:
+ execa "^1.0.0"
+ ip-regex "^2.1.0"
+
default-require-extensions@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8"
@@ -4585,17 +4735,18 @@ defined@^1.0.0:
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=
-del@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5"
- integrity sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=
+del@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4"
+ integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==
dependencies:
+ "@types/glob" "^7.1.1"
globby "^6.1.0"
- is-path-cwd "^1.0.0"
- is-path-in-cwd "^1.0.0"
- p-map "^1.1.1"
- pify "^3.0.0"
- rimraf "^2.2.8"
+ is-path-cwd "^2.0.0"
+ is-path-in-cwd "^2.0.0"
+ p-map "^2.0.0"
+ pify "^4.0.1"
+ rimraf "^2.6.3"
delayed-stream@~1.0.0:
version "1.0.0"
@@ -4853,6 +5004,11 @@ domain-browser@^1.1.1:
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
+domain-browser@^4.19.0:
+ version "4.19.0"
+ resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.19.0.tgz#1093e17c0a17dbd521182fe90d49ac1370054af1"
+ integrity sha512-fRA+BaAWOR/yr/t7T9E9GJztHPeFjj8U35ajyAjCDtAAnTn1Rc1f6W6VGPJrO1tkQv9zWu+JRof7z6oQtiYVFQ==
+
domelementtype@1, domelementtype@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
@@ -4870,15 +5026,7 @@ domhandler@^2.3.0:
dependencies:
domelementtype "1"
-domutils@1.5.1:
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
- integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=
- dependencies:
- dom-serializer "0"
- domelementtype "1"
-
-domutils@^1.5.1:
+domutils@^1.5.1, domutils@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
@@ -4886,6 +5034,14 @@ domutils@^1.5.1:
dom-serializer "0"
domelementtype "1"
+dot-case@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
+ integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==
+ dependencies:
+ no-case "^3.0.4"
+ tslib "^2.0.3"
+
duplexer2@^0.1.2, duplexer2@~0.1.0:
version "0.1.4"
resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
@@ -4893,7 +5049,7 @@ duplexer2@^0.1.2, duplexer2@~0.1.0:
dependencies:
readable-stream "^2.0.2"
-duplexify@^3.2.0, duplexify@^3.4.2, duplexify@^3.6.0:
+duplexify@^3.2.0:
version "3.7.1"
resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309"
integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==
@@ -4928,6 +5084,11 @@ electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.571:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.582.tgz#1adfac5affce84d85b3d7b3dfbc4ade293a6ffc4"
integrity sha512-0nCJ7cSqnkMC+kUuPs0YgklFHraWGl/xHqtZWWtOeVtyi+YqkoAOMGuZQad43DscXCQI/yizcTa3u6B5r+BLww==
+electron-to-chromium@^1.3.723:
+ version "1.3.728"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.728.tgz#dbedd6373f595ae10a13d146b66bece4c1afa5bd"
+ integrity sha512-SHv4ziXruBpb1Nz4aTuqEHBYi/9GNCJMYIJgDEXrp/2V01nFXMNFUTli5Z85f5ivSkioLilQatqBYFB44wNJrA==
+
elegant-spinner@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
@@ -4961,11 +5122,6 @@ emoji-regex@^8.0.0:
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
-emojis-list@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
- integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
-
emojis-list@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
@@ -4990,15 +5146,13 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
-enhanced-resolve@^3.4.0:
- version "3.4.1"
- resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e"
- integrity sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=
+enhanced-resolve@^5.8.0:
+ version "5.8.2"
+ resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz#15ddc779345cbb73e97c611cd00c01c1e7bf4d8b"
+ integrity sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==
dependencies:
- graceful-fs "^4.1.2"
- memory-fs "^0.4.0"
- object-assign "^4.0.1"
- tapable "^0.2.7"
+ graceful-fs "^4.2.4"
+ tapable "^2.2.0"
enhanced-resolve@~0.9.0:
version "0.9.1"
@@ -5026,6 +5180,11 @@ entities@^2.0.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
+envinfo@^7.7.3:
+ version "7.8.1"
+ resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475"
+ integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==
+
err-code@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/err-code/-/err-code-0.1.2.tgz#122a92b3342b9899da02b5ac994d30f95d4763ee"
@@ -5052,7 +5211,7 @@ error@^7.0.0:
dependencies:
string-template "~0.2.1"
-es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5:
+es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.5:
version "1.17.7"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c"
integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==
@@ -5109,6 +5268,11 @@ es-abstract@^1.18.0-next.2:
string.prototype.trimstart "^1.0.4"
unbox-primitive "^1.0.0"
+es-module-lexer@^0.4.0:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.4.1.tgz#dda8c6a14d8f340a24e34331e0fab0cb50438e0e"
+ integrity sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==
+
es-to-primitive@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
@@ -5118,79 +5282,17 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
-es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@~0.10.14:
- version "0.10.53"
- resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1"
- integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==
- dependencies:
- es6-iterator "~2.0.3"
- es6-symbol "~3.1.3"
- next-tick "~1.0.0"
-
-es6-iterator@^2.0.3, es6-iterator@~2.0.1, es6-iterator@~2.0.3:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
- integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c=
- dependencies:
- d "1"
- es5-ext "^0.10.35"
- es6-symbol "^3.1.1"
-
-es6-map@^0.1.3:
- version "0.1.5"
- resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0"
- integrity sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=
- dependencies:
- d "1"
- es5-ext "~0.10.14"
- es6-iterator "~2.0.1"
- es6-set "~0.1.5"
- es6-symbol "~3.1.1"
- event-emitter "~0.3.5"
+es6-object-assign@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
+ integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=
es6-promise@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-2.3.0.tgz#96edb9f2fdb01995822b263dd8aadab6748181bc"
integrity sha1-lu258v2wGZWCKyY92KratnSBgbw=
-es6-set@~0.1.5:
- version "0.1.5"
- resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1"
- integrity sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=
- dependencies:
- d "1"
- es5-ext "~0.10.14"
- es6-iterator "~2.0.1"
- es6-symbol "3.1.1"
- event-emitter "~0.3.5"
-
-es6-symbol@3.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77"
- integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=
- dependencies:
- d "1"
- es5-ext "~0.10.14"
-
-es6-symbol@^3.1.1, es6-symbol@~3.1.1, es6-symbol@~3.1.3:
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18"
- integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==
- dependencies:
- d "^1.0.1"
- ext "^1.1.2"
-
-es6-weak-map@^2.0.1:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53"
- integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==
- dependencies:
- d "1"
- es5-ext "^0.10.46"
- es6-iterator "^2.0.3"
- es6-symbol "^3.1.1"
-
-escalade@^3.1.0:
+escalade@^3.1.0, escalade@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
@@ -5217,16 +5319,6 @@ escodegen@^1.6.1:
optionalDependencies:
source-map "~0.6.1"
-escope@^3.6.0:
- version "3.6.0"
- resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3"
- integrity sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=
- dependencies:
- es6-map "^0.1.3"
- es6-weak-map "^2.0.1"
- esrecurse "^4.1.0"
- estraverse "^4.1.1"
-
eslint-import-resolver-node@^0.3.4:
version "0.3.4"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717"
@@ -5423,7 +5515,7 @@ esquery@^1.2.0:
dependencies:
estraverse "^5.1.0"
-esrecurse@^4.1.0, esrecurse@^4.3.0:
+esrecurse@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
@@ -5450,14 +5542,6 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
-event-emitter@~0.3.5:
- version "0.3.5"
- resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39"
- integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=
- dependencies:
- d "1"
- es5-ext "~0.10.14"
-
eventemitter2@^6.4.2:
version "6.4.3"
resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.3.tgz#35c563619b13f3681e7eb05cbdaf50f56ba58820"
@@ -5478,12 +5562,17 @@ events@^3.0.0:
resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379"
integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==
-eventsource@0.1.6:
- version "0.1.6"
- resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232"
- integrity sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=
+events@^3.2.0, events@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
+ integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
+
+eventsource@^1.0.7:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.1.0.tgz#00e8ca7c92109e94b0ddf32dac677d841028cfaf"
+ integrity sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==
dependencies:
- original ">=0.0.5"
+ original "^1.0.0"
evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
version "1.0.3"
@@ -5513,12 +5602,25 @@ execa@^0.7.0:
signal-exit "^3.0.0"
strip-eof "^1.0.0"
-execa@^4.0.2:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a"
- integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==
+execa@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
+ integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
dependencies:
- cross-spawn "^7.0.0"
+ cross-spawn "^6.0.0"
+ get-stream "^4.0.0"
+ is-stream "^1.1.0"
+ npm-run-path "^2.0.0"
+ p-finally "^1.0.0"
+ signal-exit "^3.0.0"
+ strip-eof "^1.0.0"
+
+execa@^4.0.2:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a"
+ integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==
+ dependencies:
+ cross-spawn "^7.0.0"
get-stream "^5.0.0"
human-signals "^1.1.1"
is-stream "^2.0.0"
@@ -5528,6 +5630,21 @@ execa@^4.0.2:
signal-exit "^3.0.2"
strip-final-newline "^2.0.0"
+execa@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376"
+ integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==
+ dependencies:
+ cross-spawn "^7.0.3"
+ get-stream "^6.0.0"
+ human-signals "^2.1.0"
+ is-stream "^2.0.0"
+ merge-stream "^2.0.0"
+ npm-run-path "^4.0.1"
+ onetime "^5.1.2"
+ signal-exit "^3.0.3"
+ strip-final-newline "^2.0.0"
+
executable@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c"
@@ -5572,7 +5689,7 @@ expand-range@^1.8.1:
dependencies:
fill-range "^2.1.0"
-express@^4.16.2:
+express@^4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
@@ -5608,13 +5725,6 @@ express@^4.16.2:
utils-merge "1.0.1"
vary "~1.1.2"
-ext@^1.1.2:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244"
- integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==
- dependencies:
- type "^2.0.0"
-
extend-shallow@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
@@ -5656,16 +5766,6 @@ extglob@^2.0.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
-extract-text-webpack-plugin@^3.0.1:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.2.tgz#5f043eaa02f9750a9258b78c0a6e0dc1408fb2f7"
- integrity sha512-bt/LZ4m5Rqt/Crl2HiKuAl/oqg0psx1tsTLkvWbJen1CtD+fftkZhMaQ9HOtY2gWsl2Wq+sABmMVi9z3DhKWQQ==
- dependencies:
- async "^2.4.1"
- loader-utils "^1.1.0"
- schema-utils "^0.3.0"
- webpack-sources "^1.0.1"
-
extract-zip@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927"
@@ -5711,25 +5811,30 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6:
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+fastest-levenshtein@^1.0.12:
+ version "1.0.12"
+ resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2"
+ integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==
+
fastparse@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==
-faye-websocket@^0.10.0, faye-websocket@~0.10.0:
- version "0.10.0"
- resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
- integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=
- dependencies:
- websocket-driver ">=0.5.1"
-
-faye-websocket@~0.11.0:
+faye-websocket@^0.11.3:
version "0.11.3"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e"
integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==
dependencies:
websocket-driver ">=0.5.1"
+faye-websocket@~0.10.0:
+ version "0.10.0"
+ resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
+ integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=
+ dependencies:
+ websocket-driver ">=0.5.1"
+
fb-watchman@^1.8.0:
version "1.9.2"
resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-1.9.2.tgz#a24cf47827f82d38fb59a69ad70b76e3b6ae7383"
@@ -5786,13 +5891,6 @@ file-entry-cache@^5.0.1:
dependencies:
flat-cache "^2.0.1"
-file-loader@^0.11.1:
- version "0.11.2"
- resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-0.11.2.tgz#4ff1df28af38719a6098093b88c82c71d1794a34"
- integrity sha512-N+uhF3mswIFeziHQjGScJ/yHXYt3DiLBeC+9vWW+WjUBiClMSOlV1YrXQi+7KM2aA3Rn4Bybgv+uXFQbfkzpvg==
- dependencies:
- loader-utils "^1.0.2"
-
file-uri-to-path@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
@@ -5832,12 +5930,10 @@ fill-range@^4.0.0:
repeat-string "^1.6.1"
to-regex-range "^2.1.0"
-fill-range@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
- integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
- dependencies:
- to-regex-range "^5.0.1"
+filter-obj@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-2.0.2.tgz#fff662368e505d69826abb113f0f6a98f56e9d5f"
+ integrity sha512-lO3ttPjHZRfjMcxWKb1j1eDhTFsu4meeR3lnMcnBFhk6RuLhvEiuALu2TlfL310ph4lCYYwgF/ElIjdP739tdg==
finalhandler@~1.1.2:
version "1.1.2"
@@ -5906,7 +6002,7 @@ find-up@^3.0.0:
dependencies:
locate-path "^3.0.0"
-find-up@^4.0.0, find-up@^4.1.0:
+find-up@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
@@ -5938,14 +6034,6 @@ flatten@^1.0.2:
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b"
integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==
-flush-write-stream@^1.0.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
- integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==
- dependencies:
- inherits "^2.0.3"
- readable-stream "^2.3.6"
-
flux-standard-action@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/flux-standard-action/-/flux-standard-action-0.6.1.tgz#6f34211b94834ea1c3cc30f4e7afad3d0fbf71a2"
@@ -5970,6 +6058,11 @@ for-own@^0.1.4:
dependencies:
for-in "^1.0.1"
+foreach@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
+ integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k=
+
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
@@ -6015,14 +6108,6 @@ fresh@0.5.2:
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
-from2@^2.1.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af"
- integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=
- dependencies:
- inherits "^2.0.1"
- readable-stream "^2.0.0"
-
fs-extra@^2.0.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-2.1.2.tgz#046c70163cef9aad46b0e4a7fa467fb22d71de35"
@@ -6065,16 +6150,6 @@ fs-readdir-recursive@^1.0.0:
resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27"
integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==
-fs-write-stream-atomic@^1.0.8:
- version "1.0.10"
- resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9"
- integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=
- dependencies:
- graceful-fs "^4.1.2"
- iferr "^0.1.5"
- imurmurhash "^0.1.4"
- readable-stream "1 || 2"
-
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -6088,11 +6163,6 @@ fsevents@^1.0.0, fsevents@^1.2.7:
bindings "^1.5.0"
nan "^2.12.1"
-fsevents@~2.1.2:
- version "2.1.3"
- resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e"
- integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==
-
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
@@ -6161,16 +6231,18 @@ get-port@^3.1.0:
resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc"
integrity sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=
-get-stdin@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
- integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=
-
get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=
+get-stream@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
+ integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
+ dependencies:
+ pump "^3.0.0"
+
get-stream@^5.0.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
@@ -6178,6 +6250,11 @@ get-stream@^5.0.0:
dependencies:
pump "^3.0.0"
+get-stream@^6.0.0:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
+ integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
+
get-value@^2.0.3, get-value@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
@@ -6236,14 +6313,6 @@ github-slugger@^1.0.0, github-slugger@^1.1.1:
dependencies:
emoji-regex ">=6.0.0 <=6.1.1"
-glob-all@^3.1.0:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/glob-all/-/glob-all-3.2.1.tgz#082ca81afd2247cbd3ed2149bb2630f4dc877d95"
- integrity sha512-x877rVkzB3ipid577QOp+eQCR6M5ZyiwrtaYgrX/z3EThaSPFtLDwBXFHc3sH1cG0R0vFYI5SRYeWMMSEyXkUw==
- dependencies:
- glob "^7.1.2"
- yargs "^15.3.1"
-
glob-base@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
@@ -6267,7 +6336,7 @@ glob-parent@^3.0.0, glob-parent@^3.1.0:
is-glob "^3.1.0"
path-dirname "^1.0.0"
-glob-parent@^5.0.0, glob-parent@~5.1.0:
+glob-parent@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
@@ -6288,6 +6357,11 @@ glob-stream@^5.3.2:
to-absolute-glob "^0.1.1"
unique-stream "^2.0.2"
+glob-to-regexp@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
+ integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
+
glob@^5.0.15, glob@^5.0.3:
version "5.0.15"
resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
@@ -6373,6 +6447,11 @@ graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.4,
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
+graceful-fs@^4.2.4:
+ version "4.2.6"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
+ integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
+
"graceful-readlink@>= 1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
@@ -6583,7 +6662,7 @@ hawk@~3.1.3:
hoek "2.x.x"
sntp "1.x.x"
-he@1.2.x:
+he@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
@@ -6669,23 +6748,23 @@ html-encoding-sniffer@^1.0.1:
dependencies:
whatwg-encoding "^1.0.1"
-html-entities@^1.2.0:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44"
- integrity sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==
-
-html-minifier@^3.2.3:
- version "3.5.21"
- resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c"
- integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==
- dependencies:
- camel-case "3.0.x"
- clean-css "4.2.x"
- commander "2.17.x"
- he "1.2.x"
- param-case "2.1.x"
- relateurl "0.2.x"
- uglify-js "3.4.x"
+html-entities@^1.3.1:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.4.0.tgz#cfbd1b01d2afaf9adca1b10ae7dffab98c71d2dc"
+ integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==
+
+html-minifier-terser@^5.0.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#922e96f1f3bb60832c2634b79884096389b1f054"
+ integrity sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==
+ dependencies:
+ camel-case "^4.1.1"
+ clean-css "^4.2.3"
+ commander "^4.1.1"
+ he "^1.2.0"
+ param-case "^3.0.3"
+ relateurl "^0.2.7"
+ terser "^4.6.3"
html-tags@^2.0.0:
version "2.0.0"
@@ -6697,26 +6776,23 @@ html-void-elements@^1.0.0:
resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483"
integrity sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==
-html-webpack-harddisk-plugin@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/html-webpack-harddisk-plugin/-/html-webpack-harddisk-plugin-0.1.0.tgz#432024961a21ac668fa2b5dfe24629c60b9c58d7"
- integrity sha1-QyAklhohrGaPorXf4kYpxgucWNc=
- dependencies:
- mkdirp "^0.5.1"
+html-webpack-harddisk-plugin@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/html-webpack-harddisk-plugin/-/html-webpack-harddisk-plugin-2.0.0.tgz#2de78316554e7aa37d07066d3687901beca4d5ce"
+ integrity sha512-fWKH72FyaQ5K/j+kYy6LnQsQucSDnsEkghmB6g29TtpJ4sxHYFduEeUV1hfDqyDpCRW+bP7yacjQ+1ikeIDqeg==
-html-webpack-plugin@^2.30.1:
- version "2.30.1"
- resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-2.30.1.tgz#7f9c421b7ea91ec460f56527d78df484ee7537d5"
- integrity sha1-f5xCG36pHsRg9WUn1430hO51N9U=
+html-webpack-plugin@^5.3.1:
+ version "5.3.1"
+ resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.3.1.tgz#8797327548e3de438e3494e0c6d06f181a7f20d1"
+ integrity sha512-rZsVvPXUYFyME0cuGkyOHfx9hmkFa4pWfxY/mdY38PsBEaVNsRoA+Id+8z6DBDgyv3zaw6XQszdF8HLwfQvcdQ==
dependencies:
- bluebird "^3.4.7"
- html-minifier "^3.2.3"
- loader-utils "^0.2.16"
- lodash "^4.17.3"
- pretty-error "^2.0.2"
- toposort "^1.0.0"
+ "@types/html-minifier-terser" "^5.0.0"
+ html-minifier-terser "^5.0.1"
+ lodash "^4.17.20"
+ pretty-error "^2.1.1"
+ tapable "^2.0.0"
-htmlparser2@^3.3.0:
+htmlparser2@^3.10.1:
version "3.10.1"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==
@@ -6770,17 +6846,17 @@ http-parser-js@>=0.5.1:
resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.2.tgz#da2e31d237b393aae72ace43882dd7e270a8ff77"
integrity sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==
-http-proxy-middleware@^0.19.1:
- version "0.19.2"
- resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.2.tgz#ee73dcc8348165afefe8de2ff717751d181608ee"
- integrity sha512-aYk1rTKqLTus23X3L96LGNCGNgWpG4cG0XoZIT1GUPhhulEHX/QalnO6Vbo+WmKWi4AL2IidjuC0wZtbpg0yhQ==
+http-proxy-middleware@0.19.1:
+ version "0.19.1"
+ resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a"
+ integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==
dependencies:
- http-proxy "^1.18.1"
+ http-proxy "^1.17.0"
is-glob "^4.0.0"
lodash "^4.17.11"
micromatch "^3.1.10"
-http-proxy@^1.18.1:
+http-proxy@^1.17.0:
version "1.18.1"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
@@ -6826,6 +6902,11 @@ human-signals@^1.1.1:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
+human-signals@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
+ integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
+
humanize-plus@^1.8.1:
version "1.8.2"
resolved "https://registry.yarnpkg.com/humanize-plus/-/humanize-plus-1.8.2.tgz#a65b34459ad6367adbb3707a82a3c9f916167030"
@@ -6872,10 +6953,10 @@ ieee754@^1.1.4:
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
-iferr@^0.1.5:
- version "0.1.5"
- resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
- integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE=
+ieee754@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
+ integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
iframe-resizer@^4.3.2:
version "4.3.2"
@@ -6937,14 +7018,22 @@ import-from@^2.1.0:
dependencies:
resolve-from "^3.0.0"
-import-local@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc"
- integrity sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==
+import-local@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d"
+ integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==
dependencies:
- pkg-dir "^2.0.0"
+ pkg-dir "^3.0.0"
resolve-cwd "^2.0.0"
+import-local@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6"
+ integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==
+ dependencies:
+ pkg-dir "^4.2.0"
+ resolve-cwd "^3.0.0"
+
imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
@@ -6985,7 +7074,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
-inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
+inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -7005,17 +7094,23 @@ ini@^1.3.0, ini@^1.3.3, ini@^1.3.5:
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
+inline-style-parser@0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1"
+ integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==
+
insert-css@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/insert-css/-/insert-css-0.2.0.tgz#d15789971662d9899c28977fb6220d5381d2451a"
integrity sha1-0VeJlxZi2YmcKJd/tiINU4HSRRo=
-internal-ip@1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-1.2.0.tgz#ae9fbf93b984878785d50a8de1b356956058cf5c"
- integrity sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w=
+internal-ip@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907"
+ integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==
dependencies:
- meow "^3.3.0"
+ default-gateway "^4.2.0"
+ ipaddr.js "^1.9.0"
internal-slot@^1.0.3:
version "1.0.3"
@@ -7031,6 +7126,11 @@ interpret@^1.0.0:
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
+interpret@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
+ integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
+
invariant@^2.0.0, invariant@^2.1.0, invariant@^2.2.1, invariant@^2.2.2, invariant@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
@@ -7043,12 +7143,17 @@ invert-kv@^1.0.0:
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY=
+ip-regex@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
+ integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=
+
ip@^1.1.0, ip@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=
-ipaddr.js@1.9.1:
+ipaddr.js@1.9.1, ipaddr.js@^1.9.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
@@ -7058,6 +7163,11 @@ is-absolute-url@^2.0.0:
resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=
+is-absolute-url@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698"
+ integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==
+
is-absolute@^0.2.3:
version "0.2.6"
resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-0.2.6.tgz#20de69f3db942ef2d87b9c2da36f172235b1b5eb"
@@ -7133,13 +7243,6 @@ is-binary-path@^1.0.0:
dependencies:
binary-extensions "^1.0.0"
-is-binary-path@~2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
- integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
- dependencies:
- binary-extensions "^2.0.0"
-
is-boolean-object@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0"
@@ -7152,6 +7255,11 @@ is-buffer@^1.1.4, is-buffer@^1.1.5:
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
+is-buffer@^2.0.0:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191"
+ integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==
+
is-callable@^1.1.4, is-callable@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9"
@@ -7286,6 +7394,11 @@ is-fullwidth-code-point@^3.0.0:
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+is-generator-function@^1.0.7:
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.9.tgz#e5f82c2323673e7fcad3d12858c83c4039f6399c"
+ integrity sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A==
+
is-glob@^2.0.0, is-glob@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
@@ -7300,7 +7413,7 @@ is-glob@^3.1.0:
dependencies:
is-extglob "^2.1.0"
-is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
+is-glob@^4.0.0, is-glob@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
@@ -7336,6 +7449,14 @@ is-my-json-valid@^2.12.4:
jsonpointer "^4.0.0"
xtend "^4.0.0"
+is-nan@^1.2.1:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d"
+ integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==
+ dependencies:
+ call-bind "^1.0.0"
+ define-properties "^1.1.3"
+
is-negative-zero@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461"
@@ -7370,11 +7491,6 @@ is-number@^4.0.0:
resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==
-is-number@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
- integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
-
is-obj@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
@@ -7387,24 +7503,24 @@ is-observable@^1.1.0:
dependencies:
symbol-observable "^1.1.0"
-is-path-cwd@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
- integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=
+is-path-cwd@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb"
+ integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==
-is-path-in-cwd@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52"
- integrity sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==
+is-path-in-cwd@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb"
+ integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==
dependencies:
- is-path-inside "^1.0.0"
+ is-path-inside "^2.1.0"
-is-path-inside@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036"
- integrity sha1-jvW33lBDej/cprToZe96pVy0gDY=
+is-path-inside@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2"
+ integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==
dependencies:
- path-is-inside "^1.0.1"
+ path-is-inside "^1.0.2"
is-path-inside@^3.0.1:
version "3.0.2"
@@ -7416,6 +7532,11 @@ is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4=
+is-plain-obj@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
+ integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
+
is-plain-object@2.0.4, is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
@@ -7513,6 +7634,17 @@ is-symbol@^1.0.2, is-symbol@^1.0.3:
dependencies:
has-symbols "^1.0.1"
+is-typed-array@^1.1.3:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.5.tgz#f32e6e096455e329eb7b423862456aa213f0eb4e"
+ integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug==
+ dependencies:
+ available-typed-arrays "^1.0.2"
+ call-bind "^1.0.2"
+ es-abstract "^1.18.0-next.2"
+ foreach "^2.0.5"
+ has-symbols "^1.0.1"
+
is-typedarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
@@ -7933,6 +8065,15 @@ jest-validate@^19.0.2:
leven "^2.0.0"
pretty-format "^19.0.0"
+jest-worker@^26.6.2:
+ version "26.6.2"
+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed"
+ integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==
+ dependencies:
+ "@types/node" "*"
+ merge-stream "^2.0.0"
+ supports-color "^7.0.0"
+
jest@^19.0.2:
version "19.0.2"
resolved "https://registry.yarnpkg.com/jest/-/jest-19.0.2.tgz#b794faaf8ff461e7388f28beef559a54f20b2c10"
@@ -8044,12 +8185,7 @@ jsesc@~0.5.0:
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
-json-loader@^0.5.4:
- version "0.5.7"
- resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d"
- integrity sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==
-
-json-parse-better-errors@^1.0.1:
+json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
@@ -8091,12 +8227,12 @@ json-stringify-safe@~5.0.1:
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
-json3@^3.3.2:
+json3@^3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81"
integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==
-json5@^0.5.0, json5@^0.5.1:
+json5@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=
@@ -8214,7 +8350,7 @@ kebab-case@^1.0.0:
resolved "https://registry.yarnpkg.com/kebab-case/-/kebab-case-1.0.0.tgz#3f9e4990adcad0c686c0e701f7645868f75f91eb"
integrity sha1-P55JkK3K0MaGwOcB92RYaPdfkes=
-killable@^1.0.0:
+killable@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==
@@ -8248,11 +8384,6 @@ lazy-ass@^1.6.0:
resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513"
integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM=
-lazy-cache@^1.0.3:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e"
- integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4=
-
lazystream@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4"
@@ -8456,20 +8587,10 @@ load-json-file@^4.0.0:
pify "^3.0.0"
strip-bom "^3.0.0"
-loader-runner@^2.3.0:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
- integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
-
-loader-utils@^0.2.16:
- version "0.2.17"
- resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348"
- integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=
- dependencies:
- big.js "^3.1.3"
- emojis-list "^2.0.0"
- json5 "^0.5.0"
- object-assign "^4.0.1"
+loader-runner@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384"
+ integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==
loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.4.0:
version "1.4.0"
@@ -8634,11 +8755,16 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
-lodash@^4.0.0, lodash@^4.0.1, lodash@^4.11.1, lodash@^4.13.1, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.5.1:
+lodash@^4.0.0, lodash@^4.0.1, lodash@^4.11.1, lodash@^4.13.1, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.5.1:
version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
+lodash@^4.17.20:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
log-symbols@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
@@ -8670,21 +8796,16 @@ log-update@^2.3.0:
cli-cursor "^2.0.0"
wrap-ansi "^3.0.1"
-loglevel@^1.4.1:
- version "1.7.0"
- resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0"
- integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ==
+loglevel@^1.6.8:
+ version "1.7.1"
+ resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197"
+ integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==
longest-streak@^2.0.1:
version "2.0.4"
resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4"
integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==
-longest@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
- integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=
-
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
@@ -8692,25 +8813,19 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
-loud-rejection@^1.0.0:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
- integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=
+lower-case@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
+ integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
dependencies:
- currently-unhandled "^0.4.1"
- signal-exit "^3.0.0"
-
-lower-case@^1.1.1:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
- integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
+ tslib "^2.0.3"
lru-cache@^2.6.5:
version "2.7.3"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952"
integrity sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=
-lru-cache@^4.0.1, lru-cache@^4.1.1:
+lru-cache@^4.0.1:
version "4.1.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
@@ -8757,11 +8872,6 @@ map-cache@^0.2.0, map-cache@^0.2.2:
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
-map-obj@^1.0.0, map-obj@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
- integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=
-
map-visit@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
@@ -8803,13 +8913,6 @@ md5.js@^1.3.4:
inherits "^2.0.1"
safe-buffer "^5.1.2"
-mdast-add-list-metadata@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/mdast-add-list-metadata/-/mdast-add-list-metadata-1.0.1.tgz#95e73640ce2fc1fa2dcb7ec443d09e2bfe7db4cf"
- integrity sha512-fB/VP4MJ0LaRsog7hGPxgOrSL3gE/2uEdZyDuSEnKCv/8IkYHiDkIQSbChiJoHyxZZXZ9bzckyRk+vNxFzh8rA==
- dependencies:
- unist-util-visit-parents "1.1.2"
-
mdast-util-compact@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/mdast-util-compact/-/mdast-util-compact-1.0.4.tgz#d531bb7667b5123abf20859be086c4d06c894593"
@@ -8824,6 +8927,24 @@ mdast-util-definitions@^1.2.0:
dependencies:
unist-util-visit "^1.0.0"
+mdast-util-definitions@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz#c5c1a84db799173b4dcf7643cda999e440c24db2"
+ integrity sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==
+ dependencies:
+ unist-util-visit "^2.0.0"
+
+mdast-util-from-markdown@^0.8.0:
+ version "0.8.5"
+ resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz#d1ef2ca42bc377ecb0463a987910dae89bd9a28c"
+ integrity sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==
+ dependencies:
+ "@types/mdast" "^3.0.0"
+ mdast-util-to-string "^2.0.0"
+ micromark "~2.11.0"
+ parse-entities "^2.0.0"
+ unist-util-stringify-position "^2.0.0"
+
mdast-util-inject@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/mdast-util-inject/-/mdast-util-inject-1.1.0.tgz#db06b8b585be959a2dcd2f87f472ba9b756f3675"
@@ -8831,6 +8952,20 @@ mdast-util-inject@^1.1.0:
dependencies:
mdast-util-to-string "^1.0.0"
+mdast-util-to-hast@^10.2.0:
+ version "10.2.0"
+ resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.2.0.tgz#61875526a017d8857b71abc9333942700b2d3604"
+ integrity sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ==
+ dependencies:
+ "@types/mdast" "^3.0.0"
+ "@types/unist" "^2.0.0"
+ mdast-util-definitions "^4.0.0"
+ mdurl "^1.0.0"
+ unist-builder "^2.0.0"
+ unist-util-generated "^1.0.0"
+ unist-util-position "^3.0.0"
+ unist-util-visit "^2.0.0"
+
mdast-util-to-hast@^2.1.1:
version "2.5.0"
resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-2.5.0.tgz#f087844d255c7540f36906da30ba106c0ee5ee2f"
@@ -8853,6 +8988,11 @@ mdast-util-to-string@^1.0.0, mdast-util-to-string@^1.0.2:
resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527"
integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==
+mdast-util-to-string@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b"
+ integrity sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==
+
mdast-util-toc@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mdast-util-toc/-/mdast-util-toc-2.1.0.tgz#82b6b218577bb0e67b23abf5c3f7ac73a4b5389f"
@@ -8862,7 +9002,7 @@ mdast-util-toc@^2.0.0:
mdast-util-to-string "^1.0.2"
unist-util-visit "^1.1.0"
-mdurl@^1.0.1:
+mdurl@^1.0.0, mdurl@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=
@@ -8872,19 +9012,12 @@ media-typer@0.3.0:
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
-mem@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76"
- integrity sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=
- dependencies:
- mimic-fn "^1.0.0"
-
memory-fs@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290"
integrity sha1-8rslNovBIeORwlIN6Slpyu4KApA=
-memory-fs@^0.4.0, memory-fs@~0.4.1:
+memory-fs@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=
@@ -8892,22 +9025,6 @@ memory-fs@^0.4.0, memory-fs@~0.4.1:
errno "^0.1.3"
readable-stream "^2.0.1"
-meow@^3.3.0:
- version "3.7.0"
- resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
- integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=
- dependencies:
- camelcase-keys "^2.0.0"
- decamelize "^1.1.2"
- loud-rejection "^1.0.0"
- map-obj "^1.0.1"
- minimist "^1.1.3"
- normalize-package-data "^2.3.4"
- object-assign "^4.0.1"
- read-pkg-up "^1.0.1"
- redent "^1.0.0"
- trim-newlines "^1.0.0"
-
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
@@ -8935,6 +9052,14 @@ methods@~1.1.2:
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
+micromark@~2.11.0:
+ version "2.11.4"
+ resolved "https://registry.yarnpkg.com/micromark/-/micromark-2.11.4.tgz#d13436138eea826383e822449c9a5c50ee44665a"
+ integrity sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==
+ dependencies:
+ debug "^4.0.0"
+ parse-entities "^2.0.0"
+
micromatch@^2.1.5, micromatch@^2.3.11, micromatch@^2.3.7:
version "2.3.11"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
@@ -8986,6 +9111,11 @@ mime-db@1.44.0:
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
+mime-db@1.47.0:
+ version "1.47.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c"
+ integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==
+
"mime-db@>= 1.43.0 < 2":
version "1.45.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea"
@@ -8998,11 +9128,23 @@ mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24,
dependencies:
mime-db "1.44.0"
-mime@1.6.0, mime@^1.2.11, mime@^1.3.4, mime@^1.5.0:
+mime-types@^2.1.27:
+ version "2.1.30"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d"
+ integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==
+ dependencies:
+ mime-db "1.47.0"
+
+mime@1.6.0, mime@^1.2.11, mime@^1.3.4:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
+mime@^2.4.4:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe"
+ integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==
+
mimic-fn@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
@@ -9025,6 +9167,15 @@ min-indent@^1.0.0:
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
+mini-css-extract-plugin@^1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.0.tgz#b4db2525af2624899ed64a23b0016e0036411893"
+ integrity sha512-nPFKI7NSy6uONUo9yn2hIfb9vyYvkFu95qki0e21DQ9uaqNKDP15DGpK0KnV6wDroWxPHtExrdEwx/yDQ8nVRw==
+ dependencies:
+ loader-utils "^2.0.0"
+ schema-utils "^3.0.0"
+ webpack-sources "^1.1.0"
+
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
@@ -9042,27 +9193,11 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
dependencies:
brace-expansion "^1.1.7"
-minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
+minimist@^1.1.0, minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
-mississippi@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f"
- integrity sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==
- dependencies:
- concat-stream "^1.5.0"
- duplexify "^3.4.2"
- end-of-stream "^1.1.0"
- flush-write-stream "^1.0.0"
- from2 "^2.1.0"
- parallel-transform "^1.1.0"
- pump "^2.0.1"
- pumpify "^1.3.3"
- stream-each "^1.1.0"
- through2 "^2.0.0"
-
mixin-deep@^1.2.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
@@ -9071,7 +9206,7 @@ mixin-deep@^1.2.0:
for-in "^1.0.2"
is-extendable "^1.0.1"
-mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.0, mkdirp@~0.5.1:
+mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.1:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
@@ -9130,18 +9265,6 @@ moment@2.x.x, "moment@>= 2.9.0", moment@^2.29.1:
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
-move-concurrently@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
- integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=
- dependencies:
- aproba "^1.1.1"
- copy-concurrently "^1.0.0"
- fs-write-stream-atomic "^1.0.8"
- mkdirp "^0.5.1"
- rimraf "^2.5.4"
- run-queue "^1.0.3"
-
ms@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
@@ -9236,22 +9359,23 @@ negotiator@0.6.2:
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
-neo-async@^2.5.0, neo-async@^2.6.0:
+neo-async@^2.6.0, neo-async@^2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
-next-tick@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
- integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
+nice-try@^1.0.4:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
+ integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
-no-case@^2.2.0:
- version "2.3.2"
- resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
- integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
+no-case@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
+ integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
dependencies:
- lower-case "^1.1.1"
+ lower-case "^2.0.2"
+ tslib "^2.0.3"
node-fetch@^1.0.1:
version "1.7.3"
@@ -9271,7 +9395,7 @@ node-int64@^0.4.0:
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=
-"node-libs-browser@^1.0.0 || ^2.0.0", node-libs-browser@^2.0.0, node-libs-browser@^2.2.1:
+"node-libs-browser@^1.0.0 || ^2.0.0", node-libs-browser@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425"
integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==
@@ -9311,17 +9435,52 @@ node-notifier@^5.0.1, node-notifier@^5.1.2:
shellwords "^0.1.1"
which "^1.3.0"
+node-polyfill-webpack-plugin@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/node-polyfill-webpack-plugin/-/node-polyfill-webpack-plugin-1.1.2.tgz#3e8e8640a920b566d5842df84f1c8c077554250e"
+ integrity sha512-ubwf7M73V13RDlKaDmMh1+giB/D8KL75umXEGabGtxSt/WRCirl01urhK1qsr6Cdt77escQ9SNb5OoTR8IwEHg==
+ dependencies:
+ assert "^2.0.0"
+ browserify-zlib "^0.2.0"
+ buffer "^6.0.3"
+ console-browserify "^1.2.0"
+ constants-browserify "^1.0.0"
+ crypto-browserify "^3.12.0"
+ domain-browser "^4.19.0"
+ events "^3.3.0"
+ filter-obj "^2.0.2"
+ https-browserify "^1.0.0"
+ os-browserify "^0.3.0"
+ path-browserify "^1.0.1"
+ process "^0.11.10"
+ punycode "^2.1.1"
+ querystring-es3 "^0.2.1"
+ readable-stream "^3.6.0"
+ stream-browserify "^3.0.0"
+ stream-http "^3.2.0"
+ string_decoder "^1.3.0"
+ timers-browserify "^2.0.12"
+ tty-browserify "^0.0.1"
+ url "^0.11.0"
+ util "^0.12.3"
+ vm-browserify "^1.1.2"
+
node-releases@^1.1.61:
version "1.1.63"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.63.tgz#db6dbb388544c31e888216304e8fd170efee3ff5"
integrity sha512-ukW3iCfQaoxJkSPN+iK7KznTeqDGVJatAEuXsJERYHa9tn/KaT5lBdIyxQjLEVTzSkyjJEuQ17/vaEjrOauDkg==
+node-releases@^1.1.71:
+ version "1.1.72"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe"
+ integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==
+
node-uuid@~1.4.7:
version "1.4.8"
resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907"
integrity sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=
-normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
+normalize-package-data@^2.3.2:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
@@ -9338,7 +9497,7 @@ normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1:
dependencies:
remove-trailing-separator "^1.0.1"
-normalize-path@^3.0.0, normalize-path@~3.0.0:
+normalize-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
@@ -9377,7 +9536,7 @@ npm-run-path@^2.0.0:
dependencies:
path-key "^2.0.0"
-npm-run-path@^4.0.0:
+npm-run-path@^4.0.0, npm-run-path@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
@@ -9393,7 +9552,7 @@ npm-which@^3.0.1:
npm-path "^2.0.2"
which "^1.2.10"
-nth-check@~1.0.1:
+nth-check@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
@@ -9519,14 +9678,6 @@ object.fromentries@^2.0.2:
es-abstract "^1.18.0-next.2"
has "^1.0.3"
-object.getownpropertydescriptors@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649"
- integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==
- dependencies:
- define-properties "^1.1.3"
- es-abstract "^1.17.0-next.1"
-
object.omit@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
@@ -9598,14 +9749,14 @@ onetime@^2.0.0:
dependencies:
mimic-fn "^1.0.0"
-onetime@^5.1.0:
+onetime@^5.1.0, onetime@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
dependencies:
mimic-fn "^2.1.0"
-opn@^5.1.0:
+opn@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc"
integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==
@@ -9659,7 +9810,7 @@ ordered-read-streams@^0.3.0:
is-stream "^1.0.1"
readable-stream "^2.0.1"
-original@>=0.0.5:
+original@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f"
integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==
@@ -9683,15 +9834,6 @@ os-locale@^1.4.0:
dependencies:
lcid "^1.0.0"
-os-locale@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2"
- integrity sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==
- dependencies:
- execa "^0.7.0"
- lcid "^1.0.0"
- mem "^1.1.0"
-
os-tmpdir@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
@@ -9730,6 +9872,13 @@ p-limit@^2.0.0, p-limit@^2.2.0:
dependencies:
p-try "^2.0.0"
+p-limit@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
+ integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
+ dependencies:
+ yocto-queue "^0.1.0"
+
p-locate@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
@@ -9761,6 +9910,13 @@ p-map@^2.0.0:
resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==
+p-retry@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328"
+ integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==
+ dependencies:
+ retry "^0.12.0"
+
p-try@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
@@ -9776,21 +9932,13 @@ pako@~1.0.5:
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
-parallel-transform@^1.1.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc"
- integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==
- dependencies:
- cyclist "^1.0.1"
- inherits "^2.0.3"
- readable-stream "^2.1.5"
-
-param-case@2.1.x:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
- integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc=
+param-case@^3.0.3:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5"
+ integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==
dependencies:
- no-case "^2.2.0"
+ dot-case "^3.0.4"
+ tslib "^2.0.3"
parent-module@^1.0.0:
version "1.0.1"
@@ -9817,7 +9965,7 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.5:
pbkdf2 "^3.0.3"
safe-buffer "^5.1.1"
-parse-entities@^1.0.2, parse-entities@^1.1.0:
+parse-entities@^1.0.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.2.2.tgz#c31bf0f653b6661354f8973559cb86dd1d5edf50"
integrity sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg==
@@ -9829,14 +9977,26 @@ parse-entities@^1.0.2, parse-entities@^1.1.0:
is-decimal "^1.0.0"
is-hexadecimal "^1.0.0"
-parse-filepath@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891"
- integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=
+parse-entities@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8"
+ integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==
dependencies:
- is-absolute "^1.0.0"
- map-cache "^0.2.0"
- path-root "^0.1.1"
+ character-entities "^1.0.0"
+ character-entities-legacy "^1.0.0"
+ character-reference-invalid "^1.0.0"
+ is-alphanumerical "^1.0.0"
+ is-decimal "^1.0.0"
+ is-hexadecimal "^1.0.0"
+
+parse-filepath@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891"
+ integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=
+ dependencies:
+ is-absolute "^1.0.0"
+ map-cache "^0.2.0"
+ path-root "^0.1.1"
parse-git-config@^0.2.0:
version "0.2.0"
@@ -9908,6 +10068,14 @@ parseurl@~1.3.2, parseurl@~1.3.3:
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+pascal-case@^3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb"
+ integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==
+ dependencies:
+ no-case "^3.0.4"
+ tslib "^2.0.3"
+
pascalcase@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
@@ -9923,6 +10091,11 @@ path-browserify@0.0.1:
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
+path-browserify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
+ integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
+
path-dirname@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
@@ -9950,12 +10123,12 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
-path-is-inside@^1.0.1:
+path-is-inside@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
-path-key@^2.0.0:
+path-key@^2.0.0, path-key@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
@@ -10046,11 +10219,6 @@ performance-now@^2.1.0:
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
-picomatch@^2.0.4, picomatch@^2.2.1:
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
- integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
-
pify@^2.0.0, pify@^2.2.0, pify@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@@ -10101,7 +10269,7 @@ pkg-dir@^3.0.0:
dependencies:
find-up "^3.0.0"
-pkg-dir@^4.1.0:
+pkg-dir@^4.1.0, pkg-dir@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
@@ -10126,7 +10294,7 @@ plural-forms@0.3.3:
resolved "https://registry.yarnpkg.com/plural-forms/-/plural-forms-0.3.3.tgz#22d9cddf3fe50d7f6930e3aa1eea339bdc27e152"
integrity sha512-9CEFxpHa1KrtZFoa0FybZcxyDx1ceWelowtlSWtFsmRzBgQ9Dj47YmyY6cynPAJUoA/kLm2B7VYi0CfNIDZnCw==
-portfinder@^1.0.9:
+portfinder@^1.0.26:
version "1.0.28"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778"
integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==
@@ -10747,13 +10915,13 @@ pretty-bytes@^5.4.1:
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.4.1.tgz#cd89f79bbcef21e3d21eb0da68ffe93f803e884b"
integrity sha512-s1Iam6Gwz3JI5Hweaz4GoCD1WUNUIyzePFy5+Js2hjwGVt2Z79wNN+ZKOZ2vB6C+Xs6njyB84Z1IthQg8d9LxA==
-pretty-error@^2.0.2:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3"
- integrity sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=
+pretty-error@^2.1.1:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.2.tgz#be89f82d81b1c86ec8fdfbc385045882727f93b6"
+ integrity sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==
dependencies:
- renderkid "^2.0.1"
- utila "~0.4"
+ lodash "^4.17.20"
+ renderkid "^2.0.4"
pretty-format@^19.0.0:
version "19.0.0"
@@ -10839,11 +11007,6 @@ promise-each@^2.2.0:
dependencies:
any-promise "^0.1.0"
-promise-inflight@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
- integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM=
-
promise-loader@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/promise-loader/-/promise-loader-1.0.0.tgz#6fc7c8529c1fdfc497bef5fe7448bb61af9546cc"
@@ -10877,6 +11040,13 @@ property-information@^3.1.0:
resolved "https://registry.yarnpkg.com/property-information/-/property-information-3.2.0.tgz#fd1483c8fbac61808f5fe359e7693a1f48a58331"
integrity sha1-/RSDyPusYYCPX+NZ52k6H0ilgzE=
+property-information@^5.0.0:
+ version "5.6.0"
+ resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69"
+ integrity sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==
+ dependencies:
+ xtend "^4.0.0"
+
protocols@^1.1.0, protocols@^1.4.0:
version "1.4.8"
resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8"
@@ -10917,14 +11087,6 @@ public-encrypt@^4.0.0:
randombytes "^2.0.1"
safe-buffer "^5.1.2"
-pump@^2.0.0, pump@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909"
- integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==
- dependencies:
- end-of-stream "^1.1.0"
- once "^1.3.1"
-
pump@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
@@ -10933,15 +11095,6 @@ pump@^3.0.0:
end-of-stream "^1.1.0"
once "^1.3.1"
-pumpify@^1.3.3:
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce"
- integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==
- dependencies:
- duplexify "^3.6.0"
- inherits "^2.0.3"
- pump "^2.0.0"
-
punycode@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
@@ -10990,7 +11143,7 @@ query-string@^4.1.0, query-string@^4.2.2:
object-assign "^4.1.0"
strict-uri-encode "^1.0.0"
-querystring-es3@^0.2.0:
+querystring-es3@^0.2.0, querystring-es3@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=
@@ -11026,7 +11179,7 @@ randomatic@^3.0.0:
kind-of "^6.0.0"
math-random "^1.0.1"
-randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
+randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
@@ -11041,7 +11194,7 @@ randomfill@^1.0.3:
randombytes "^2.0.5"
safe-buffer "^5.1.0"
-range-parser@^1.0.3, range-parser@~1.2.1:
+range-parser@^1.2.1, range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
@@ -11191,6 +11344,11 @@ react-is@^16.12.0, react-is@^16.13.0, react-is@^16.3.1, react-is@^16.6.0, react-
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+react-is@^17.0.0:
+ version "17.0.2"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
+ integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
+
react-is@^17.0.1:
version "17.0.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
@@ -11208,17 +11366,24 @@ react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4:
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
-react-markdown@^3.6.0:
- version "3.6.0"
- resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-3.6.0.tgz#29f6aaab5270c8ef0a5e234093a873ec3e01722b"
- integrity sha512-TV0wQDHHPCEeKJHWXFfEAKJ8uSEsJ9LgrMERkXx05WV/3q6Ig+59KDNaTmjcoqlCpE/sH5PqqLMh4t0QWKrJ8Q==
+react-markdown@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-6.0.2.tgz#d89be45c278b1e5f0196f851fffb11e30c69f027"
+ integrity sha512-Et2AjXAsbmPP1nLQQRqmVgcqzfwcz8uQJ8VAdADs8Nk/aaUA0YeU9RDLuCtD+GwajCnm/+Iiu2KPmXzmD/M3vA==
dependencies:
- mdast-add-list-metadata "1.0.1"
- prop-types "^15.6.1"
- remark-parse "^5.0.0"
- unified "^6.1.5"
- unist-util-visit "^1.3.0"
- xtend "^4.0.1"
+ "@types/hast" "^2.0.0"
+ "@types/unist" "^2.0.3"
+ comma-separated-tokens "^1.0.0"
+ prop-types "^15.7.2"
+ property-information "^5.0.0"
+ react-is "^17.0.0"
+ remark-parse "^9.0.0"
+ remark-rehype "^8.0.0"
+ space-separated-tokens "^1.1.0"
+ style-to-object "^0.3.0"
+ unified "^9.0.0"
+ unist-util-visit "^2.0.0"
+ vfile "^4.0.0"
react-motion@^0.4.5:
version "0.4.8"
@@ -11383,7 +11548,17 @@ read-pkg@^3.0.0:
normalize-package-data "^2.3.2"
path-type "^3.0.0"
-"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6:
+"readable-stream@>=1.0.33-1 <1.1.0-0":
+ version "1.0.34"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
+ integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=
+ dependencies:
+ core-util-is "~1.0.0"
+ inherits "~2.0.1"
+ isarray "0.0.1"
+ string_decoder "~0.10.x"
+
+readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@@ -11396,17 +11571,7 @@ read-pkg@^3.0.0:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
-"readable-stream@>=1.0.33-1 <1.1.0-0":
- version "1.0.34"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
- integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=
- dependencies:
- core-util-is "~1.0.0"
- inherits "~2.0.1"
- isarray "0.0.1"
- string_decoder "~0.10.x"
-
-readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.2.0, readable-stream@^3.6.0:
+readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.2.0, readable-stream@^3.5.0, readable-stream@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@@ -11449,13 +11614,6 @@ readdirp@^2.0.0, readdirp@^2.2.1:
micromatch "^3.1.10"
readable-stream "^2.0.2"
-readdirp@~3.5.0:
- version "3.5.0"
- resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e"
- integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==
- dependencies:
- picomatch "^2.2.1"
-
readline-sync@^1.4.7:
version "1.4.10"
resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b"
@@ -11488,13 +11646,12 @@ rechoir@^0.6.2:
dependencies:
resolve "^1.1.6"
-redent@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"
- integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=
+rechoir@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.0.tgz#32650fd52c21ab252aa5d65b19310441c7e03aca"
+ integrity sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==
dependencies:
- indent-string "^2.1.0"
- strip-indent "^1.0.1"
+ resolve "^1.9.0"
redent@^3.0.0:
version "3.0.0"
@@ -11763,7 +11920,7 @@ regjsparser@^0.6.4:
dependencies:
jsesc "~0.5.0"
-relateurl@0.2.x:
+relateurl@^0.2.7:
version "0.2.7"
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
@@ -11799,26 +11956,19 @@ remark-parse@^4.0.0:
vfile-location "^2.0.0"
xtend "^4.0.1"
-remark-parse@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-5.0.0.tgz#4c077f9e499044d1d5c13f80d7a98cf7b9285d95"
- integrity sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA==
+remark-parse@^9.0.0:
+ version "9.0.0"
+ resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-9.0.0.tgz#4d20a299665880e4f4af5d90b7c7b8a935853640"
+ integrity sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==
dependencies:
- collapse-white-space "^1.0.2"
- is-alphabetical "^1.0.0"
- is-decimal "^1.0.0"
- is-whitespace-character "^1.0.0"
- is-word-character "^1.0.0"
- markdown-escapes "^1.0.0"
- parse-entities "^1.1.0"
- repeat-string "^1.5.4"
- state-toggle "^1.0.0"
- trim "0.0.1"
- trim-trailing-lines "^1.0.0"
- unherit "^1.0.4"
- unist-util-remove-position "^1.0.0"
- vfile-location "^2.0.0"
- xtend "^4.0.1"
+ mdast-util-from-markdown "^0.8.0"
+
+remark-rehype@^8.0.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-8.1.0.tgz#610509a043484c1e697437fa5eb3fd992617c945"
+ integrity sha512-EbCu9kHgAxKmW1yEYjx3QafMyGY3q8noUbNUI5xyKbaFP89wbhDrKxyIQNukNYthzjNHZu6J7hwFg7hRm1svYA==
+ dependencies:
+ mdast-util-to-hast "^10.2.0"
remark-slug@^4.0.0:
version "4.2.3"
@@ -11878,16 +12028,16 @@ remove-trailing-separator@^1.0.1:
resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
-renderkid@^2.0.1:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.3.tgz#380179c2ff5ae1365c522bf2fcfcff01c5b74149"
- integrity sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==
+renderkid@^2.0.4:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.5.tgz#483b1ac59c6601ab30a7a596a5965cabccfdd0a5"
+ integrity sha512-ccqoLg+HLOHq1vdfYNm4TBeaCDIi1FLt3wGojTDSvdewUv65oTmI3cnT2E4hRjl1gzKZIPK+KZrXzlUYKnR+vQ==
dependencies:
- css-select "^1.1.0"
+ css-select "^2.0.2"
dom-converter "^0.2"
- htmlparser2 "^3.3.0"
+ htmlparser2 "^3.10.1"
+ lodash "^4.17.20"
strip-ansi "^3.0.0"
- utila "^0.4.0"
repeat-element@^1.1.2:
version "1.1.3"
@@ -12022,6 +12172,13 @@ resolve-cwd@^2.0.0:
dependencies:
resolve-from "^3.0.0"
+resolve-cwd@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
+ integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==
+ dependencies:
+ resolve-from "^5.0.0"
+
resolve-from@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
@@ -12032,6 +12189,11 @@ resolve-from@^4.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+resolve-from@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
+ integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
+
resolve-url@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
@@ -12049,7 +12211,7 @@ resolve@^1.1.3, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.12.0
dependencies:
path-parse "^1.0.6"
-resolve@^1.18.1:
+resolve@^1.18.1, resolve@^1.9.0:
version "1.20.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
@@ -12078,6 +12240,11 @@ ret@~0.1.10:
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
+retry@^0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
+ integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
+
retry@^0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/retry/-/retry-0.9.0.tgz#6f697e50a0e4ddc8c8f7fb547a9b60dead43678d"
@@ -12093,13 +12260,6 @@ rgb@~0.1.0:
resolved "https://registry.yarnpkg.com/rgb/-/rgb-0.1.0.tgz#be27b291e8feffeac1bd99729721bfa40fc037b5"
integrity sha1-vieykej+/+rBvZlylyG/pA/AN7U=
-right-align@^0.1.1:
- version "0.1.3"
- resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
- integrity sha1-YTObci/mo1FWiSENJOFMlhSGE+8=
- dependencies:
- align-text "^0.1.1"
-
rimraf@2.6.3:
version "2.6.3"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
@@ -12107,7 +12267,7 @@ rimraf@2.6.3:
dependencies:
glob "^7.1.3"
-rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2:
+rimraf@^2.6.1, rimraf@^2.6.3:
version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
@@ -12129,13 +12289,6 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0"
inherits "^2.0.1"
-run-queue@^1.0.0, run-queue@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47"
- integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=
- dependencies:
- aproba "^1.1.1"
-
rx@2.3.24:
version "2.3.24"
resolved "https://registry.yarnpkg.com/rx/-/rx-2.3.24.tgz#14f950a4217d7e35daa71bbcbe58eff68ea4b2b7"
@@ -12215,7 +12368,7 @@ schema-utils@^0.3.0:
dependencies:
ajv "^5.0.0"
-schema-utils@^0.4.0, schema-utils@^0.4.5:
+schema-utils@^0.4.0:
version "0.4.7"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187"
integrity sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==
@@ -12223,6 +12376,15 @@ schema-utils@^0.4.0, schema-utils@^0.4.5:
ajv "^6.1.0"
ajv-keywords "^3.1.0"
+schema-utils@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"
+ integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==
+ dependencies:
+ ajv "^6.1.0"
+ ajv-errors "^1.0.0"
+ ajv-keywords "^3.1.0"
+
schema-utils@^2.6.5:
version "2.7.1"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7"
@@ -12232,6 +12394,15 @@ schema-utils@^2.6.5:
ajv "^6.12.4"
ajv-keywords "^3.5.2"
+schema-utils@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef"
+ integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==
+ dependencies:
+ "@types/json-schema" "^7.0.6"
+ ajv "^6.12.5"
+ ajv-keywords "^3.5.2"
+
screenfull@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-4.2.1.tgz#3245b7bc73d2b7c9a15bd8caaf6965db7cbc7f04"
@@ -12247,10 +12418,10 @@ select-hose@^2.0.0:
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=
-selfsigned@^1.9.1:
- version "1.10.8"
- resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.8.tgz#0d17208b7d12c33f8eac85c41835f27fc3d81a30"
- integrity sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==
+selfsigned@^1.10.8:
+ version "1.10.11"
+ resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.11.tgz#24929cd906fe0f44b6d01fb23999a739537acbe9"
+ integrity sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==
dependencies:
node-forge "^0.10.0"
@@ -12264,7 +12435,7 @@ semver@7.0.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
-semver@^6.0.0:
+semver@^6.0.0, semver@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
@@ -12298,10 +12469,12 @@ send@0.17.1:
range-parser "~1.2.1"
statuses "~1.5.0"
-serialize-javascript@^1.4.0:
- version "1.9.1"
- resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.9.1.tgz#cfc200aef77b600c47da9bb8149c943e798c2fdb"
- integrity sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==
+serialize-javascript@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4"
+ integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==
+ dependencies:
+ randombytes "^2.1.0"
serve-index@^1.9.1:
version "1.9.1"
@@ -12381,6 +12554,13 @@ shadow-cljs@2.11.20:
which "^1.3.1"
ws "^3.0.0"
+shallow-clone@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
+ integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
+ dependencies:
+ kind-of "^6.0.2"
+
shallowequal@^1.0.2, shallowequal@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
@@ -12433,7 +12613,7 @@ side-channel@^1.0.4:
get-intrinsic "^1.0.2"
object-inspect "^1.9.0"
-signal-exit@^3.0.0, signal-exit@^3.0.2:
+signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
@@ -12511,25 +12691,26 @@ sntp@1.x.x:
dependencies:
hoek "2.x.x"
-sockjs-client@1.1.5:
- version "1.1.5"
- resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.5.tgz#1bb7c0f7222c40f42adf14f4442cbd1269771a83"
- integrity sha1-G7fA9yIsQPQq3xT0RCy9Eml3GoM=
+sockjs-client@^1.5.0:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.5.1.tgz#256908f6d5adfb94dabbdbd02c66362cca0f9ea6"
+ integrity sha512-VnVAb663fosipI/m6pqRXakEOw7nvd7TUgdr3PlR/8V2I95QIdwT8L4nMxhyU8SmDBHYXU1TOElaKOmKLfYzeQ==
dependencies:
- debug "^2.6.6"
- eventsource "0.1.6"
- faye-websocket "~0.11.0"
- inherits "^2.0.1"
- json3 "^3.3.2"
- url-parse "^1.1.8"
+ debug "^3.2.6"
+ eventsource "^1.0.7"
+ faye-websocket "^0.11.3"
+ inherits "^2.0.4"
+ json3 "^3.3.3"
+ url-parse "^1.5.1"
-sockjs@0.3.19:
- version "0.3.19"
- resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d"
- integrity sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==
+sockjs@^0.3.21:
+ version "0.3.21"
+ resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.21.tgz#b34ffb98e796930b60a0cfa11904d6a339a7d417"
+ integrity sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==
dependencies:
- faye-websocket "^0.10.0"
- uuid "^3.0.1"
+ faye-websocket "^0.11.3"
+ uuid "^3.4.0"
+ websocket-driver "^0.7.4"
sort-keys@^1.0.0:
version "1.1.2"
@@ -12538,7 +12719,7 @@ sort-keys@^1.0.0:
dependencies:
is-plain-obj "^1.0.0"
-source-list-map@^2.0.0:
+source-list-map@^2.0.0, source-list-map@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
@@ -12561,22 +12742,30 @@ source-map-support@^0.4.15:
dependencies:
source-map "^0.5.6"
+source-map-support@~0.5.12, source-map-support@~0.5.19:
+ version "0.5.19"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
+ integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
+ dependencies:
+ buffer-from "^1.0.0"
+ source-map "^0.6.0"
+
source-map-url@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
-source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0, source-map@~0.5.1:
+source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
-source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
+source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
-source-map@^0.7.3:
+source-map@^0.7.3, source-map@~0.7.2:
version "0.7.3"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
@@ -12588,7 +12777,7 @@ source-map@~0.4.2:
dependencies:
amdefine ">=0.0.4"
-space-separated-tokens@^1.0.0:
+space-separated-tokens@^1.0.0, space-separated-tokens@^1.1.0:
version "1.1.5"
resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899"
integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==
@@ -12636,7 +12825,7 @@ spdy-transport@^3.0.0:
readable-stream "^3.0.6"
wbuf "^1.7.3"
-spdy@^4.0.0:
+spdy@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b"
integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==
@@ -12681,13 +12870,6 @@ sshpk@^1.7.0:
safer-buffer "^2.0.2"
tweetnacl "~0.14.0"
-ssri@^5.2.4:
- version "5.3.0"
- resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.3.0.tgz#ba3872c9c6d33a0704a7d71ff045e5ec48999d06"
- integrity sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==
- dependencies:
- safe-buffer "^5.1.1"
-
stack-trace@0.0.x:
version "0.0.10"
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
@@ -12731,6 +12913,14 @@ stream-browserify@^2.0.1:
inherits "~2.0.1"
readable-stream "^2.0.2"
+stream-browserify@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f"
+ integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==
+ dependencies:
+ inherits "~2.0.4"
+ readable-stream "^3.5.0"
+
stream-combiner2@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe"
@@ -12739,14 +12929,6 @@ stream-combiner2@^1.1.1:
duplexer2 "~0.1.0"
readable-stream "^2.0.2"
-stream-each@^1.1.0:
- version "1.2.3"
- resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae"
- integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==
- dependencies:
- end-of-stream "^1.1.0"
- stream-shift "^1.0.0"
-
stream-http@^2.7.2:
version "2.8.3"
resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc"
@@ -12758,6 +12940,16 @@ stream-http@^2.7.2:
to-arraybuffer "^1.0.0"
xtend "^4.0.0"
+stream-http@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.2.0.tgz#1872dfcf24cb15752677e40e5c3f9cc1926028b5"
+ integrity sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==
+ dependencies:
+ builtin-status-codes "^3.0.0"
+ inherits "^2.0.4"
+ readable-stream "^3.6.0"
+ xtend "^4.0.2"
+
stream-shift@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
@@ -12794,7 +12986,7 @@ string-width@^1.0.0, string-width@^1.0.1, string-width@^1.0.2:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
-string-width@^2.0.0, string-width@^2.1.1:
+string-width@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
@@ -12802,7 +12994,7 @@ string-width@^2.0.0, string-width@^2.1.1:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
-string-width@^3.0.0:
+string-width@^3.0.0, string-width@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
@@ -12811,7 +13003,7 @@ string-width@^3.0.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^5.1.0"
-string-width@^4.1.0, string-width@^4.2.0:
+string-width@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5"
integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==
@@ -12870,7 +13062,7 @@ string_decoder@0.10, string_decoder@~0.10.x:
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=
-string_decoder@^1.0.0, string_decoder@^1.1.1:
+string_decoder@^1.0.0, string_decoder@^1.1.1, string_decoder@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
@@ -12922,7 +13114,7 @@ strip-ansi@^4.0.0:
dependencies:
ansi-regex "^3.0.0"
-strip-ansi@^5.1.0:
+strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
@@ -12966,13 +13158,6 @@ strip-final-newline@^2.0.0:
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
-strip-indent@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2"
- integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=
- dependencies:
- get-stdin "^4.0.1"
-
strip-indent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001"
@@ -12998,6 +13183,13 @@ style-loader@^0.19.0:
loader-utils "^1.0.2"
schema-utils "^0.3.0"
+style-to-object@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46"
+ integrity sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==
+ dependencies:
+ inline-style-parser "0.1.1"
+
styled-components@3.2.6:
version "3.2.6"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-3.2.6.tgz#99e6e75a746bdedd295a17e03dd1493055a1cc3b"
@@ -13050,21 +13242,28 @@ supports-color@^3.1.2, supports-color@^3.2.3:
dependencies:
has-flag "^1.0.0"
-supports-color@^4.1.0, supports-color@^4.2.1:
+supports-color@^4.1.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b"
integrity sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=
dependencies:
has-flag "^2.0.0"
-supports-color@^5.1.0, supports-color@^5.3.0, supports-color@^5.4.0:
+supports-color@^5.3.0, supports-color@^5.4.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
dependencies:
has-flag "^3.0.0"
-supports-color@^7.1.0, supports-color@^7.2.0:
+supports-color@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
+ integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
+ dependencies:
+ has-flag "^3.0.0"
+
+supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
@@ -13114,10 +13313,40 @@ tapable@^0.1.8:
resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4"
integrity sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=
-tapable@^0.2.7:
- version "0.2.9"
- resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.9.tgz#af2d8bbc9b04f74ee17af2b4d9048f807acd18a8"
- integrity sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A==
+tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b"
+ integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==
+
+terser-webpack-plugin@^5.1.1:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.2.tgz#51d295eb7cc56785a67a372575fdc46e42d5c20c"
+ integrity sha512-6QhDaAiVHIQr5Ab3XUWZyDmrIPCHMiqJVljMF91YKyqwKkL5QHnYMkrMBy96v9Z7ev1hGhSEw1HQZc2p/s5Z8Q==
+ dependencies:
+ jest-worker "^26.6.2"
+ p-limit "^3.1.0"
+ schema-utils "^3.0.0"
+ serialize-javascript "^5.0.1"
+ source-map "^0.6.1"
+ terser "^5.7.0"
+
+terser@^4.6.3:
+ version "4.8.0"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17"
+ integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==
+ dependencies:
+ commander "^2.20.0"
+ source-map "~0.6.1"
+ source-map-support "~0.5.12"
+
+terser@^5.7.0:
+ version "5.7.0"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-5.7.0.tgz#a761eeec206bc87b605ab13029876ead938ae693"
+ integrity sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g==
+ dependencies:
+ commander "^2.20.0"
+ source-map "~0.7.2"
+ source-map-support "~0.5.19"
test-exclude@^4.2.1:
version "4.2.3"
@@ -13206,10 +13435,12 @@ thunky@^1.0.2:
resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
-time-stamp@^2.0.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.2.0.tgz#917e0a66905688790ec7bbbde04046259af83f57"
- integrity sha512-zxke8goJQpBeEgD82CXABeMh0LSJcj7CXEd0OHOg45HgcofF7pxNwZm9+RknpxpDhwN4gFpySkApKfFYfRQnUA==
+timers-browserify@^2.0.12:
+ version "2.0.12"
+ resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee"
+ integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==
+ dependencies:
+ setimmediate "^1.0.4"
timers-browserify@^2.0.4:
version "2.0.11"
@@ -13301,13 +13532,6 @@ to-regex-range@^2.1.0:
is-number "^3.0.0"
repeat-string "^1.6.1"
-to-regex-range@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
- integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
- dependencies:
- is-number "^7.0.0"
-
to-regex@^3.0.1, to-regex@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
@@ -13342,11 +13566,6 @@ topo@1.x.x:
dependencies:
hoek "2.x.x"
-toposort@^1.0.0:
- version "1.0.7"
- resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029"
- integrity sha1-LmhELZ9k7HILjMieZEOsbKqVACk=
-
tough-cookie@^2.3.2, tough-cookie@~2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
@@ -13377,11 +13596,6 @@ trim-lines@^1.0.0:
resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-1.1.3.tgz#839514be82428fd9e7ec89e35081afe8f6f93115"
integrity sha512-E0ZosSWYK2mkSu+KEtQ9/KqarVjA9HztOSX+9FDdNacRAq29RRV6ZQNgob3iuW8Htar9vAfEa6yyt5qBAHZDBA==
-trim-newlines@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
- integrity sha1-WIeWa7WCpFA6QetST301ARgVphM=
-
trim-right@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
@@ -13417,6 +13631,11 @@ tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
+tslib@^2.0.3:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
+ integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
+
ttag@1.7.15:
version "1.7.15"
resolved "https://registry.yarnpkg.com/ttag/-/ttag-1.7.15.tgz#e0a97283071bc945b1cde09a7cd3b82a0d0921a6"
@@ -13430,6 +13649,11 @@ tty-browserify@0.0.0:
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=
+tty-browserify@^0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811"
+ integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==
+
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
@@ -13474,16 +13698,6 @@ type-is@~1.6.17, type-is@~1.6.18:
media-typer "0.3.0"
mime-types "~2.1.24"
-type@^1.0.1:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"
- integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==
-
-type@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/type/-/type-2.1.0.tgz#9bdc22c648cf8cf86dd23d32336a41cfb6475e3f"
- integrity sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==
-
typedarray@^0.0.6, typedarray@~0.0.5:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
@@ -13494,65 +13708,11 @@ ua-parser-js@^0.7.18:
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.22.tgz#960df60a5f911ea8f1c818f3747b99c6e177eae3"
integrity sha512-YUxzMjJ5T71w6a8WWVcMGM6YWOTX27rCoIQgLXiWaxqXSx9D7DNjiGWn1aJIRSQ5qr0xuhra77bSIh6voR/46Q==
-uglify-es@^3.3.4:
- version "3.3.9"
- resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"
- integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==
- dependencies:
- commander "~2.13.0"
- source-map "~0.6.1"
-
-uglify-js@3.4.x:
- version "3.4.10"
- resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f"
- integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==
- dependencies:
- commander "~2.19.0"
- source-map "~0.6.1"
-
-uglify-js@^2.8.29:
- version "2.8.29"
- resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
- integrity sha1-KcVzMUgFe7Th913zW3qcty5qWd0=
- dependencies:
- source-map "~0.5.1"
- yargs "~3.10.0"
- optionalDependencies:
- uglify-to-browserify "~1.0.0"
-
uglify-js@^3.1.4:
version "3.11.2"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.11.2.tgz#9f50325544273c27b20e586def140e7726c525ea"
integrity sha512-G440NU6fewtnQftSgqRV1r2A5ChKbU1gqFCJ7I8S7MPpY/eZZfLGefaY6gUZYiWebMaO+txgiQ1ZyLDuNWJulg==
-uglify-to-browserify@~1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
- integrity sha1-bgkk1r2mta/jSeOabWMoUKD4grc=
-
-uglifyjs-webpack-plugin@^0.4.6:
- version "0.4.6"
- resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz#b951f4abb6bd617e66f63eb891498e391763e309"
- integrity sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=
- dependencies:
- source-map "^0.5.6"
- uglify-js "^2.8.29"
- webpack-sources "^1.0.1"
-
-uglifyjs-webpack-plugin@^1.0.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz#75f548160858163a08643e086d5fefe18a5d67de"
- integrity sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==
- dependencies:
- cacache "^10.0.4"
- find-cache-dir "^1.0.0"
- schema-utils "^0.4.5"
- serialize-javascript "^1.4.0"
- source-map "^0.6.1"
- uglify-es "^3.3.4"
- webpack-sources "^1.1.0"
- worker-farm "^1.5.2"
-
ultron@1.0.x:
version "1.0.2"
resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa"
@@ -13614,7 +13774,7 @@ unicode-property-aliases-ecmascript@^1.0.4:
resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4"
integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==
-unified@^6.0.0, unified@^6.1.5:
+unified@^6.0.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/unified/-/unified-6.2.0.tgz#7fbd630f719126d67d40c644b7e3f617035f6dba"
integrity sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==
@@ -13626,6 +13786,18 @@ unified@^6.0.0, unified@^6.1.5:
vfile "^2.0.0"
x-is-string "^0.1.0"
+unified@^9.0.0:
+ version "9.2.1"
+ resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.1.tgz#ae18d5674c114021bfdbdf73865ca60f410215a3"
+ integrity sha512-juWjuI8Z4xFg8pJbnEZ41b5xjGUWGHqXALmBZ3FC3WX0PIx1CZBIIJ6mXbYMcf6Yw4Fi0rFUTA1cdz/BglbOhA==
+ dependencies:
+ bail "^1.0.0"
+ extend "^3.0.0"
+ is-buffer "^2.0.0"
+ is-plain-obj "^2.0.0"
+ trough "^1.0.0"
+ vfile "^4.0.0"
+
union-value@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847"
@@ -13646,20 +13818,6 @@ uniqs@^2.0.0:
resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI=
-unique-filename@^1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230"
- integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==
- dependencies:
- unique-slug "^2.0.0"
-
-unique-slug@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c"
- integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==
- dependencies:
- imurmurhash "^0.1.4"
-
unique-stream@^2.0.2:
version "2.3.1"
resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.3.1.tgz#c65d110e9a4adf9a6c5948b28053d9a8d04cbeac"
@@ -13675,6 +13833,16 @@ unist-builder@^1.0.0, unist-builder@^1.0.1:
dependencies:
object-assign "^4.1.0"
+unist-builder@^2.0.0:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436"
+ integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==
+
+unist-util-generated@^1.0.0:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b"
+ integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==
+
unist-util-generated@^1.1.0:
version "1.1.5"
resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.5.tgz#1e903e68467931ebfaea386dae9ea253628acd42"
@@ -13690,6 +13858,11 @@ unist-util-is@^3.0.0:
resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-3.0.0.tgz#d9e84381c2468e82629e4a5be9d7d05a2dd324cd"
integrity sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==
+unist-util-is@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797"
+ integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==
+
unist-util-position@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47"
@@ -13707,10 +13880,12 @@ unist-util-stringify-position@^1.0.0, unist-util-stringify-position@^1.1.1:
resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz#3f37fcf351279dcbca7480ab5889bb8a832ee1c6"
integrity sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==
-unist-util-visit-parents@1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-1.1.2.tgz#f6e3afee8bdbf961c0e6f028ea3c0480028c3d06"
- integrity sha512-yvo+MMLjEwdc3RhhPYSximset7rwjMrdt9E41Smmvg25UQIenzrN83cRnF1JMzoMi9zZOQeYXHSDf7p+IQkW3Q==
+unist-util-stringify-position@^2.0.0:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da"
+ integrity sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==
+ dependencies:
+ "@types/unist" "^2.0.2"
unist-util-visit-parents@^2.0.0:
version "2.1.2"
@@ -13719,13 +13894,30 @@ unist-util-visit-parents@^2.0.0:
dependencies:
unist-util-is "^3.0.0"
-unist-util-visit@^1.0.0, unist-util-visit@^1.0.1, unist-util-visit@^1.1.0, unist-util-visit@^1.3.0:
+unist-util-visit-parents@^3.0.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6"
+ integrity sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==
+ dependencies:
+ "@types/unist" "^2.0.0"
+ unist-util-is "^4.0.0"
+
+unist-util-visit@^1.0.0, unist-util-visit@^1.0.1, unist-util-visit@^1.1.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.1.tgz#4724aaa8486e6ee6e26d7ff3c8685960d560b1e3"
integrity sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==
dependencies:
unist-util-visit-parents "^2.0.0"
+unist-util-visit@^2.0.0:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c"
+ integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==
+ dependencies:
+ "@types/unist" "^2.0.0"
+ unist-util-is "^4.0.0"
+ unist-util-visit-parents "^3.0.0"
+
units-css@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/units-css/-/units-css-0.4.0.tgz#d6228653a51983d7c16ff28f8b9dc3b1ffed3a07"
@@ -13767,27 +13959,11 @@ untildify@^4.0.0:
resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
-unused-files-webpack-plugin@^3.0.0:
- version "3.4.0"
- resolved "https://registry.yarnpkg.com/unused-files-webpack-plugin/-/unused-files-webpack-plugin-3.4.0.tgz#adc67a3b5549d028818d3119cbf2b5c88aea8670"
- integrity sha512-cmukKOBdIqaM1pqThY0+jp+mYgCVyzrD8uRbKEucQwIGZcLIRn+gSRiQ7uLjcDd3Zba9NUxVGyYa7lWM4UCGeg==
- dependencies:
- babel-runtime "^7.0.0-beta.3"
- glob-all "^3.1.0"
- semver "^5.5.0"
- util.promisify "^1.0.0"
- warning "^3.0.0"
-
upath@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
-upper-case@^1.1.1:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
- integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=
-
uri-js@^4.2.2:
version "4.4.0"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602"
@@ -13805,7 +13981,7 @@ url-join@0.0.1:
resolved "https://registry.yarnpkg.com/url-join/-/url-join-0.0.1.tgz#1db48ad422d3402469a87f7d97bdebfe4fb1e3c8"
integrity sha1-HbSK1CLTQCRpqH99l73r/k+x48g=
-url-parse@^1.1.8, url-parse@^1.4.3:
+url-parse@^1.4.3:
version "1.4.7"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
@@ -13813,6 +13989,14 @@ url-parse@^1.1.8, url-parse@^1.4.3:
querystringify "^2.1.1"
requires-port "^1.0.0"
+url-parse@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.1.tgz#d5fa9890af8a5e1f274a2c98376510f6425f6e3b"
+ integrity sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==
+ dependencies:
+ querystringify "^2.1.1"
+ requires-port "^1.0.0"
+
url@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
@@ -13836,16 +14020,6 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
-util.promisify@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee"
- integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==
- dependencies:
- define-properties "^1.1.3"
- es-abstract "^1.17.2"
- has-symbols "^1.0.1"
- object.getownpropertydescriptors "^2.1.0"
-
util@0.10.3:
version "0.10.3"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
@@ -13860,7 +14034,19 @@ util@^0.11.0:
dependencies:
inherits "2.0.3"
-utila@^0.4.0, utila@~0.4:
+util@^0.12.0, util@^0.12.3:
+ version "0.12.4"
+ resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253"
+ integrity sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==
+ dependencies:
+ inherits "^2.0.3"
+ is-arguments "^1.0.4"
+ is-generator-function "^1.0.7"
+ is-typed-array "^1.1.3"
+ safe-buffer "^5.1.2"
+ which-typed-array "^1.1.2"
+
+utila@~0.4:
version "0.4.0"
resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c"
integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=
@@ -13870,7 +14056,7 @@ utils-merge@1.0.1:
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
-uuid@^3.0.1, uuid@^3.3.2:
+uuid@^3.3.2, uuid@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
@@ -13880,6 +14066,11 @@ v8-compile-cache@^2.0.3:
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132"
integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==
+v8-compile-cache@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
+ integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
+
v8flags@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4"
@@ -13931,6 +14122,14 @@ vfile-message@^1.0.0:
dependencies:
unist-util-stringify-position "^1.1.1"
+vfile-message@^2.0.0:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a"
+ integrity sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==
+ dependencies:
+ "@types/unist" "^2.0.0"
+ unist-util-stringify-position "^2.0.0"
+
vfile-reporter@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/vfile-reporter/-/vfile-reporter-4.0.0.tgz#ea6f0ae1342f4841573985e05f941736f27de9da"
@@ -13962,6 +14161,16 @@ vfile@^2.0.0:
unist-util-stringify-position "^1.0.0"
vfile-message "^1.0.0"
+vfile@^4.0.0:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624"
+ integrity sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==
+ dependencies:
+ "@types/unist" "^2.0.0"
+ is-buffer "^2.0.0"
+ unist-util-stringify-position "^2.0.0"
+ vfile-message "^2.0.0"
+
viewport-dimensions@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/viewport-dimensions/-/viewport-dimensions-0.2.0.tgz#de740747db5387fd1725f5175e91bac76afdf36c"
@@ -14011,7 +14220,7 @@ vinyl@^2.0.0:
remove-trailing-separator "^1.0.1"
replace-ext "^1.0.0"
-vm-browserify@^1.0.1:
+vm-browserify@^1.0.1, vm-browserify@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
@@ -14040,23 +14249,13 @@ watch@~0.10.0:
resolved "https://registry.yarnpkg.com/watch/-/watch-0.10.0.tgz#77798b2da0f9910d595f1ace5b0c2258521f21dc"
integrity sha1-d3mLLaD5kQ1ZXxrOWwwiWFIfIdw=
-watchpack-chokidar2@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz#9948a1866cbbd6cb824dea13a7ed691f6c8ddff0"
- integrity sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==
- dependencies:
- chokidar "^2.1.8"
-
-watchpack@^1.4.0:
- version "1.7.4"
- resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.4.tgz#6e9da53b3c80bb2d6508188f5b200410866cd30b"
- integrity sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==
+watchpack@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.1.1.tgz#e99630550fca07df9f90a06056987baa40a689c7"
+ integrity sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw==
dependencies:
+ glob-to-regexp "^0.4.1"
graceful-fs "^4.1.2"
- neo-async "^2.5.0"
- optionalDependencies:
- chokidar "^3.4.1"
- watchpack-chokidar2 "^2.0.0"
wbuf@^1.1.0, wbuf@^1.7.3:
version "1.7.3"
@@ -14075,49 +14274,90 @@ webidl-conversions@^4.0.0:
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
-webpack-dev-middleware@1.12.2:
- version "1.12.2"
- resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz#f8fc1120ce3b4fc5680ceecb43d777966b21105e"
- integrity sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==
- dependencies:
- memory-fs "~0.4.1"
- mime "^1.5.0"
- path-is-absolute "^1.0.0"
- range-parser "^1.0.3"
- time-stamp "^2.0.0"
+webpack-cli@^4.7.0:
+ version "4.7.0"
+ resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.7.0.tgz#3195a777f1f802ecda732f6c95d24c0004bc5a35"
+ integrity sha512-7bKr9182/sGfjFm+xdZSwgQuFjgEcy0iCTIBxRUeteJ2Kr8/Wz0qNJX+jw60LU36jApt4nmMkep6+W5AKhok6g==
+ dependencies:
+ "@discoveryjs/json-ext" "^0.5.0"
+ "@webpack-cli/configtest" "^1.0.3"
+ "@webpack-cli/info" "^1.2.4"
+ "@webpack-cli/serve" "^1.4.0"
+ colorette "^1.2.1"
+ commander "^7.0.0"
+ execa "^5.0.0"
+ fastest-levenshtein "^1.0.12"
+ import-local "^3.0.2"
+ interpret "^2.2.0"
+ rechoir "^0.7.0"
+ v8-compile-cache "^2.2.0"
+ webpack-merge "^5.7.3"
+
+webpack-dev-middleware@^3.7.2:
+ version "3.7.3"
+ resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5"
+ integrity sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==
+ dependencies:
+ memory-fs "^0.4.1"
+ mime "^2.4.4"
+ mkdirp "^0.5.1"
+ range-parser "^1.2.1"
+ webpack-log "^2.0.0"
-webpack-dev-server@^2.9.1:
- version "2.11.5"
- resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.11.5.tgz#416fbdea0e04eebe44a626e791d5a2eb37fe8c48"
- integrity sha512-7TdOKKt7G3sWEhPKV0zP+nD0c4V9YKUJ3wDdBwQsZNo58oZIRoVIu66pg7PYkBW8A74msP9C2kLwmxGHndz/pw==
+webpack-dev-server@^3.11.2:
+ version "3.11.2"
+ resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz#695ebced76a4929f0d5de7fd73fafe185fe33708"
+ integrity sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ==
dependencies:
ansi-html "0.0.7"
- array-includes "^3.0.3"
bonjour "^3.5.0"
- chokidar "^2.1.2"
- compression "^1.7.3"
- connect-history-api-fallback "^1.3.0"
- debug "^3.1.0"
- del "^3.0.0"
- express "^4.16.2"
- html-entities "^1.2.0"
- http-proxy-middleware "^0.19.1"
- import-local "^1.0.0"
- internal-ip "1.2.0"
+ chokidar "^2.1.8"
+ compression "^1.7.4"
+ connect-history-api-fallback "^1.6.0"
+ debug "^4.1.1"
+ del "^4.1.1"
+ express "^4.17.1"
+ html-entities "^1.3.1"
+ http-proxy-middleware "0.19.1"
+ import-local "^2.0.0"
+ internal-ip "^4.3.0"
ip "^1.1.5"
- killable "^1.0.0"
- loglevel "^1.4.1"
- opn "^5.1.0"
- portfinder "^1.0.9"
- selfsigned "^1.9.1"
+ is-absolute-url "^3.0.3"
+ killable "^1.0.1"
+ loglevel "^1.6.8"
+ opn "^5.5.0"
+ p-retry "^3.0.1"
+ portfinder "^1.0.26"
+ schema-utils "^1.0.0"
+ selfsigned "^1.10.8"
+ semver "^6.3.0"
serve-index "^1.9.1"
- sockjs "0.3.19"
- sockjs-client "1.1.5"
- spdy "^4.0.0"
- strip-ansi "^3.0.0"
- supports-color "^5.1.0"
- webpack-dev-middleware "1.12.2"
- yargs "6.6.0"
+ sockjs "^0.3.21"
+ sockjs-client "^1.5.0"
+ spdy "^4.0.2"
+ strip-ansi "^3.0.1"
+ supports-color "^6.1.0"
+ url "^0.11.0"
+ webpack-dev-middleware "^3.7.2"
+ webpack-log "^2.0.0"
+ ws "^6.2.1"
+ yargs "^13.3.2"
+
+webpack-log@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f"
+ integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==
+ dependencies:
+ ansi-colors "^3.0.0"
+ uuid "^3.3.2"
+
+webpack-merge@^5.7.3:
+ version "5.7.3"
+ resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.7.3.tgz#2a0754e1877a25a8bbab3d2475ca70a052708213"
+ integrity sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA==
+ dependencies:
+ clone-deep "^4.0.1"
+ wildcard "^2.0.0"
webpack-notifier@^1.8.0:
version "1.8.0"
@@ -14137,7 +14377,7 @@ webpack-postcss-tools@^1.1.2:
postcss "^4.1.7"
resolve "^1.1.6"
-webpack-sources@^1.0.1, webpack-sources@^1.1.0:
+webpack-sources@^1.1.0:
version "1.4.3"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==
@@ -14145,35 +14385,44 @@ webpack-sources@^1.0.1, webpack-sources@^1.1.0:
source-list-map "^2.0.0"
source-map "~0.6.1"
-webpack@^3.8.1:
- version "3.12.0"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.12.0.tgz#3f9e34360370602fcf639e97939db486f4ec0d74"
- integrity sha512-Sw7MdIIOv/nkzPzee4o0EdvCuPmxT98+vVpIvwtcwcF1Q4SDSNp92vwcKc4REe7NItH9f1S4ra9FuQ7yuYZ8bQ==
+webpack-sources@^2.1.1:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac"
+ integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==
dependencies:
- acorn "^5.0.0"
- acorn-dynamic-import "^2.0.0"
- ajv "^6.1.0"
- ajv-keywords "^3.1.0"
- async "^2.1.2"
- enhanced-resolve "^3.4.0"
- escope "^3.6.0"
- interpret "^1.0.0"
- json-loader "^0.5.4"
- json5 "^0.5.1"
- loader-runner "^2.3.0"
- loader-utils "^1.1.0"
- memory-fs "~0.4.1"
- mkdirp "~0.5.0"
- node-libs-browser "^2.0.0"
- source-map "^0.5.3"
- supports-color "^4.2.1"
- tapable "^0.2.7"
- uglifyjs-webpack-plugin "^0.4.6"
- watchpack "^1.4.0"
- webpack-sources "^1.0.1"
- yargs "^8.0.2"
-
-websocket-driver@>=0.5.1:
+ source-list-map "^2.0.1"
+ source-map "^0.6.1"
+
+webpack@^5.37.0:
+ version "5.37.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.37.0.tgz#2ab00f613faf494504eb2beef278dab7493cc39d"
+ integrity sha512-yvdhgcI6QkQkDe1hINBAJ1UNevqNGTVaCkD2SSJcB8rcrNNl922RI8i2DXUAuNfANoxwsiXXEA4ZPZI9q2oGLA==
+ dependencies:
+ "@types/eslint-scope" "^3.7.0"
+ "@types/estree" "^0.0.47"
+ "@webassemblyjs/ast" "1.11.0"
+ "@webassemblyjs/wasm-edit" "1.11.0"
+ "@webassemblyjs/wasm-parser" "1.11.0"
+ acorn "^8.2.1"
+ browserslist "^4.14.5"
+ chrome-trace-event "^1.0.2"
+ enhanced-resolve "^5.8.0"
+ es-module-lexer "^0.4.0"
+ eslint-scope "^5.1.1"
+ events "^3.2.0"
+ glob-to-regexp "^0.4.1"
+ graceful-fs "^4.2.4"
+ json-parse-better-errors "^1.0.2"
+ loader-runner "^4.2.0"
+ mime-types "^2.1.27"
+ neo-async "^2.6.2"
+ schema-utils "^3.0.0"
+ tapable "^2.1.1"
+ terser-webpack-plugin "^5.1.1"
+ watchpack "^2.0.0"
+ webpack-sources "^2.1.1"
+
+websocket-driver@>=0.5.1, websocket-driver@^0.7.4:
version "0.7.4"
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760"
integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==
@@ -14233,6 +14482,19 @@ which-module@^2.0.0:
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
+which-typed-array@^1.1.2:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff"
+ integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==
+ dependencies:
+ available-typed-arrays "^1.0.2"
+ call-bind "^1.0.0"
+ es-abstract "^1.18.0-next.1"
+ foreach "^2.0.5"
+ function-bind "^1.1.1"
+ has-symbols "^1.0.1"
+ is-typed-array "^1.1.3"
+
which@^1.1.1, which@^1.2.10, which@^1.2.9, which@^1.3.0, which@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
@@ -14247,10 +14509,10 @@ which@^2.0.1:
dependencies:
isexe "^2.0.0"
-window-size@0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
- integrity sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=
+wildcard@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec"
+ integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==
window-size@^0.1.4:
version "0.1.4"
@@ -14284,17 +14546,12 @@ word@~0.3.0:
resolved "https://registry.yarnpkg.com/word/-/word-0.3.0.tgz#8542157e4f8e849f4a363a288992d47612db9961"
integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==
-wordwrap@0.0.2:
- version "0.0.2"
- resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
- integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=
-
wordwrap@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
-worker-farm@^1.3.1, worker-farm@^1.5.2:
+worker-farm@^1.3.1:
version "1.7.0"
resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8"
integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==
@@ -14317,14 +14574,14 @@ wrap-ansi@^3.0.1:
string-width "^2.1.1"
strip-ansi "^4.0.0"
-wrap-ansi@^6.2.0:
- version "6.2.0"
- resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
- integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
+wrap-ansi@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09"
+ integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==
dependencies:
- ansi-styles "^4.0.0"
- string-width "^4.1.0"
- strip-ansi "^6.0.0"
+ ansi-styles "^3.2.0"
+ string-width "^3.0.0"
+ strip-ansi "^5.0.0"
wrappy@1:
version "1.0.2"
@@ -14355,6 +14612,13 @@ ws@^3.0.0:
safe-buffer "~5.1.0"
ultron "~1.1.0"
+ws@^6.2.1:
+ version "6.2.1"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
+ integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
+ dependencies:
+ async-limiter "~1.0.0"
+
x-is-string@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82"
@@ -14388,7 +14652,7 @@ xml-name-validator@^2.0.1:
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635"
integrity sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=
-"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1:
+"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
@@ -14432,10 +14696,10 @@ yaml@^1.7.2:
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e"
integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==
-yargs-parser@^18.1.2:
- version "18.1.3"
- resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
- integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
+yargs-parser@^13.1.2:
+ version "13.1.2"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"
+ integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==
dependencies:
camelcase "^5.0.0"
decamelize "^1.2.0"
@@ -14447,48 +14711,21 @@ yargs-parser@^4.2.0:
dependencies:
camelcase "^3.0.0"
-yargs-parser@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9"
- integrity sha1-jQrELxbqVd69MyyvTEA4s+P139k=
- dependencies:
- camelcase "^4.1.0"
-
-yargs@6.6.0, yargs@^6.0.1, yargs@^6.3.0:
- version "6.6.0"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208"
- integrity sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=
- dependencies:
- camelcase "^3.0.0"
- cliui "^3.2.0"
- decamelize "^1.1.1"
- get-caller-file "^1.0.1"
- os-locale "^1.4.0"
- read-pkg-up "^1.0.1"
- require-directory "^2.1.1"
- require-main-filename "^1.0.1"
- set-blocking "^2.0.0"
- string-width "^1.0.2"
- which-module "^1.0.0"
- y18n "^3.2.1"
- yargs-parser "^4.2.0"
-
-yargs@^15.3.1:
- version "15.4.1"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
- integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
+yargs@^13.3.2:
+ version "13.3.2"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"
+ integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==
dependencies:
- cliui "^6.0.0"
- decamelize "^1.2.0"
- find-up "^4.1.0"
+ cliui "^5.0.0"
+ find-up "^3.0.0"
get-caller-file "^2.0.1"
require-directory "^2.1.1"
require-main-filename "^2.0.0"
set-blocking "^2.0.0"
- string-width "^4.2.0"
+ string-width "^3.0.0"
which-module "^2.0.0"
y18n "^4.0.0"
- yargs-parser "^18.1.2"
+ yargs-parser "^13.1.2"
yargs@^3.19.0:
version "3.32.0"
@@ -14503,34 +14740,29 @@ yargs@^3.19.0:
window-size "^0.1.4"
y18n "^3.2.0"
-yargs@^8.0.2:
- version "8.0.2"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360"
- integrity sha1-YpmpBVsc78lp/355wdkY3Osiw2A=
+yargs@^6.0.1, yargs@^6.3.0:
+ version "6.6.0"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208"
+ integrity sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=
dependencies:
- camelcase "^4.1.0"
+ camelcase "^3.0.0"
cliui "^3.2.0"
decamelize "^1.1.1"
get-caller-file "^1.0.1"
- os-locale "^2.0.0"
- read-pkg-up "^2.0.0"
+ os-locale "^1.4.0"
+ read-pkg-up "^1.0.1"
require-directory "^2.1.1"
require-main-filename "^1.0.1"
set-blocking "^2.0.0"
- string-width "^2.0.0"
- which-module "^2.0.0"
+ string-width "^1.0.2"
+ which-module "^1.0.0"
y18n "^3.2.1"
- yargs-parser "^7.0.0"
+ yargs-parser "^4.2.0"
-yargs@~3.10.0:
- version "3.10.0"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"
- integrity sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=
- dependencies:
- camelcase "^1.0.2"
- cliui "^2.1.0"
- decamelize "^1.0.0"
- window-size "0.1.0"
+yarn.lock@^0.0.1-security:
+ version "0.0.1-security"
+ resolved "https://registry.yarnpkg.com/yarn.lock/-/yarn.lock-0.0.1-security.tgz#8e7117924bfe916671b21f14212ba1bb49dfe0c7"
+ integrity sha512-ZRX6v5zGCJMI1T2aO+BQxJggy1vvorXEwonQhWXIC+brO7lkDB3zWelVNAti183ddH6FmJP8z4UDCJnJlioK4Q==
yauzl@^2.10.0:
version "2.10.0"
@@ -14540,6 +14772,11 @@ yauzl@^2.10.0:
buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0"
+yocto-queue@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
+ integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+
z-index@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/z-index/-/z-index-0.0.1.tgz#4f3d257a36869dabd990572b70494291cb3eab8f"
From 2ee56db865087eff0328fe5e19e0b367f75f8cce Mon Sep 17 00:00:00 2001
From: Ariya Hidayat
Date: Mon, 19 Jul 2021 15:50:13 -0700
Subject: [PATCH 064/664] Don't use extend with styled-components anymore
(#17048)
It's deprecated and thus removed in v4:
https://styled-components.com/docs/api#deprecated-extend
---
frontend/src/metabase/components/BulkActionBar.jsx | 3 ++-
frontend/src/metabase/components/EntityItem.styled.js | 2 +-
frontend/src/metabase/components/IconWrapper.jsx | 3 ++-
frontend/src/metabase/components/PaginationControls.jsx | 3 ++-
frontend/src/metabase/components/Position.js | 8 ++++----
frontend/src/metabase/nav/components/SearchBar.jsx | 2 +-
.../query_builder/components/notebook/NotebookStep.jsx | 4 +++-
7 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/frontend/src/metabase/components/BulkActionBar.jsx b/frontend/src/metabase/components/BulkActionBar.jsx
index 87aeb9f391c4..1cd0fa90f8be 100644
--- a/frontend/src/metabase/components/BulkActionBar.jsx
+++ b/frontend/src/metabase/components/BulkActionBar.jsx
@@ -1,10 +1,11 @@
/* eslint-disable react/prop-types */
import React from "react";
+import styled from "styled-components";
import { Box } from "grid-styled";
import Card from "metabase/components/Card";
import { Motion, spring } from "react-motion";
-const FixedBottomBar = Box.extend`
+const FixedBottomBar = styled(Box)`
position: fixed;
bottom: 0;
left: 0;
diff --git a/frontend/src/metabase/components/EntityItem.styled.js b/frontend/src/metabase/components/EntityItem.styled.js
index 1d815e5ec6d5..bc58a655b288 100644
--- a/frontend/src/metabase/components/EntityItem.styled.js
+++ b/frontend/src/metabase/components/EntityItem.styled.js
@@ -37,7 +37,7 @@ export const EntityIconWrapper = styled(IconButtonWrapper)`
: getBackground(props.model)};
`;
-export const EntityItemWrapper = Flex.extend`
+export const EntityItemWrapper = styled(Flex)`
border-bottom: 1px solid ${color("bg-medium")};
align-items: center;
&:hover {
diff --git a/frontend/src/metabase/components/IconWrapper.jsx b/frontend/src/metabase/components/IconWrapper.jsx
index 8056d639f593..4aa00165468d 100644
--- a/frontend/src/metabase/components/IconWrapper.jsx
+++ b/frontend/src/metabase/components/IconWrapper.jsx
@@ -1,8 +1,9 @@
+import styled from "styled-components";
import { Flex } from "grid-styled";
import { color } from "styled-system";
import { color as metabaseColors } from "metabase/lib/colors";
-const IconWrapper = Flex.extend`
+const IconWrapper = styled(Flex)`
${color};
border-radius: ${props => props.borderRadius};
`;
diff --git a/frontend/src/metabase/components/PaginationControls.jsx b/frontend/src/metabase/components/PaginationControls.jsx
index 036514d09e08..2548bade9a85 100644
--- a/frontend/src/metabase/components/PaginationControls.jsx
+++ b/frontend/src/metabase/components/PaginationControls.jsx
@@ -1,6 +1,7 @@
import React from "react";
import PropTypes from "prop-types";
import { t } from "ttag";
+import styled from "styled-components";
import colors from "metabase/lib/colors";
import Icon, { IconWrapper } from "metabase/components/Icon";
@@ -54,7 +55,7 @@ export default function PaginationControls({
);
}
-const PaginationButton = IconWrapper.withComponent("button").extend`
+const PaginationButton = styled(IconWrapper.withComponent("button"))`
&:disabled {
background-color: transparent;
color: ${colors["text-light"]};
diff --git a/frontend/src/metabase/components/Position.js b/frontend/src/metabase/components/Position.js
index e311dc7ae03f..4ee4491f16d3 100644
--- a/frontend/src/metabase/components/Position.js
+++ b/frontend/src/metabase/components/Position.js
@@ -15,24 +15,24 @@ export const Position = styled(Box)`
Position.displayName = "Position";
-export const Relative = Position.extend`
+export const Relative = styled(Position)`
position: relative;
`;
Relative.displayName = "Relative";
-export const Absolute = Position.extend`
+export const Absolute = styled(Position)`
position: absolute;
`;
Absolute.displayName = "Absolute";
-export const Fixed = Position.extend`
+export const Fixed = styled(Position)`
position: fixed;
`;
Fixed.displayName = "Fixed";
-export const Sticky = Position.extend`
+export const Sticky = styled(Position)`
position: sticky;
`;
diff --git a/frontend/src/metabase/nav/components/SearchBar.jsx b/frontend/src/metabase/nav/components/SearchBar.jsx
index b0e22c92c30e..e72455b2e9ac 100644
--- a/frontend/src/metabase/nav/components/SearchBar.jsx
+++ b/frontend/src/metabase/nav/components/SearchBar.jsx
@@ -20,7 +20,7 @@ const ActiveSearchColor = lighten(color("nav"), 0.1);
import Search from "metabase/entities/search";
-const SearchWrapper = Flex.extend`
+const SearchWrapper = styled(Flex)`
position: relative;
background-color: ${props =>
props.active ? ActiveSearchColor : DefaultSearchColor};
diff --git a/frontend/src/metabase/query_builder/components/notebook/NotebookStep.jsx b/frontend/src/metabase/query_builder/components/notebook/NotebookStep.jsx
index 321143315582..827d814adb8b 100644
--- a/frontend/src/metabase/query_builder/components/notebook/NotebookStep.jsx
+++ b/frontend/src/metabase/query_builder/components/notebook/NotebookStep.jsx
@@ -4,6 +4,8 @@ import React from "react";
import { t } from "ttag";
import _ from "underscore";
+import styled from "styled-components";
+
import { color as c, lighten, darken } from "metabase/lib/colors";
import Tooltip from "metabase/components/Tooltip";
@@ -205,7 +207,7 @@ export default class NotebookStep extends React.Component {
}
}
-const ColorButton = Button.extend`
+const ColorButton = styled(Button)`
border: none;
color: ${({ color }) => (color ? color : c("text-medium"))}
background-color: ${({ color }) => (color ? lighten(color, 0.61) : null)};
From 648996f69a88f7b71f1e8c11d8f1db7152628654 Mon Sep 17 00:00:00 2001
From: Alexander Lesnenko
Date: Tue, 20 Jul 2021 16:20:45 +0300
Subject: [PATCH 065/664] Fix partial commits (#17121)
---
package.json | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/package.json b/package.json
index 73e5b4502abf..1e2538205f55 100644
--- a/package.json
+++ b/package.json
@@ -206,8 +206,7 @@
},
"lint-staged": {
"frontend/**/*.{js,jsx,css}": [
- "prettier --write",
- "git add"
+ "prettier --write"
]
},
"resolutions": {
From 8b83319dc8902fd759a79909c08f786c06776f60 Mon Sep 17 00:00:00 2001
From: Alexander Polyankin
Date: Tue, 20 Jul 2021 18:34:06 +0300
Subject: [PATCH 066/664] Fix padding for Add a map button (#17120)
---
.../admin/settings/components/widgets/CustomGeoJSONWidget.jsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/frontend/src/metabase/admin/settings/components/widgets/CustomGeoJSONWidget.jsx b/frontend/src/metabase/admin/settings/components/widgets/CustomGeoJSONWidget.jsx
index 148dbad1b78c..bfc46acadc03 100644
--- a/frontend/src/metabase/admin/settings/components/widgets/CustomGeoJSONWidget.jsx
+++ b/frontend/src/metabase/admin/settings/components/widgets/CustomGeoJSONWidget.jsx
@@ -140,11 +140,11 @@ export default class CustomGeoJSONWidget extends Component {
return (
-
+
{!this.state.map && (
From b5d48172472c2bd32157e1df088b6504a4f545a0 Mon Sep 17 00:00:00 2001
From: Alexander Lesnenko
Date: Tue, 20 Jul 2021 20:14:27 +0300
Subject: [PATCH 069/664] fix stacked buttons on embedded dashboards (#17126)
---
.../src/metabase/public/containers/PublicDashboard.jsx | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/frontend/src/metabase/public/containers/PublicDashboard.jsx b/frontend/src/metabase/public/containers/PublicDashboard.jsx
index e86080ff249e..bb70947eb0e1 100644
--- a/frontend/src/metabase/public/containers/PublicDashboard.jsx
+++ b/frontend/src/metabase/public/containers/PublicDashboard.jsx
@@ -151,15 +151,7 @@ export default class PublicDashboard extends Component {
parameterValues={parameterValues}
setParameterValue={this.props.setParameterValue}
actionButtons={
- buttons.length > 0 && (
-
- {buttons.map((button, index) => (
-
- {button}
-
- ))}
-
- )
+ buttons.length > 0 && {buttons}
}
>
Date: Tue, 20 Jul 2021 16:02:54 -0700
Subject: [PATCH 070/664] Don't guess site url from requests to healthcheck
endpoint (#17136)
---
src/metabase/server/middleware/misc.clj | 15 +++---
test/metabase/server/middleware/misc_test.clj | 50 +++++++++++++------
2 files changed, 42 insertions(+), 23 deletions(-)
diff --git a/src/metabase/server/middleware/misc.clj b/src/metabase/server/middleware/misc.clj
index 351fa03172d0..1ff8f10f30bb 100644
--- a/src/metabase/server/middleware/misc.clj
+++ b/src/metabase/server/middleware/misc.clj
@@ -1,7 +1,7 @@
(ns metabase.server.middleware.misc
"Misc Ring middleware."
- (:require [clojure.tools.logging :as log]
- [metabase.api.common :as api]
+ (:require [clojure.string :as str]
+ [clojure.tools.logging :as log]
metabase.async.streaming-response
[metabase.db :as mdb]
[metabase.public-settings :as public-settings]
@@ -12,7 +12,7 @@
(comment metabase.async.streaming-response/keep-me)
-(defn- add-content-type* [request {:keys [body], {:strs [Content-Type]} :headers, :as response}]
+(defn- add-content-type* [{:keys [body], {:strs [Content-Type]} :headers, :as response}]
(cond-> response
(not Content-Type)
(assoc-in [:headers "Content-Type"] (if (string? body)
@@ -27,7 +27,7 @@
(handler request
(if-not (request.u/api-call? request)
respond
- (comp respond (partial add-content-type* request)))
+ (comp respond add-content-type*))
raise)))
@@ -39,10 +39,11 @@
;;
;; Effectively the very first API request that gets sent to us (usually some sort of setup request) ends up setting
;; the (initial) value of `site-url`
-(defn- maybe-set-site-url* [{{:strs [origin x-forwarded-host host user-agent] :as headers} :headers, :as request}]
+(defn- maybe-set-site-url* [{{:strs [origin x-forwarded-host host user-agent]} :headers, uri :uri}]
(when (and (mdb/db-is-set-up?)
(not (public-settings/site-url))
- api/*current-user* (or (nil? user-agent) ((complement clojure.string/includes?) user-agent "HealthChecker"))); Not setting URL if it's a healthcheck by ELB
+ (not= uri "/api/health")
+ (or (nil? user-agent) ((complement str/includes?) user-agent "HealthChecker")))
(when-let [site-url (or origin x-forwarded-host host)]
(log/info (trs "Setting Metabase site URL to {0}" site-url))
(try
@@ -51,7 +52,7 @@
(log/warn e (trs "Failed to set site-url")))))))
(defn maybe-set-site-url
- "Middleware to set the `site-url` Setting if it's unset the first time a request is made."
+ "Middleware to set the `site-url` setting on the initial setup request"
[handler]
(fn [request respond raise]
(maybe-set-site-url* request)
diff --git a/test/metabase/server/middleware/misc_test.clj b/test/metabase/server/middleware/misc_test.clj
index 93daf616b2d6..f491fe544ac2 100644
--- a/test/metabase/server/middleware/misc_test.clj
+++ b/test/metabase/server/middleware/misc_test.clj
@@ -6,21 +6,39 @@
[metabase.test :as mt]
[ring.mock.request :as ring.mock]))
+(defn- maybe-set-site-url
+ [request]
+ ((mw.misc/maybe-set-site-url (fn [request respond _] (respond request)))
+ request
+ identity
+ (fn [e] (throw e))))
+
+(defn- mock-request
+ [uri origin-header x-forwarded-host-header host-header]
+ (cond-> (m/dissoc-in (ring.mock/request :get uri) [:headers "host"])
+ origin-header (ring.mock/header "Origin" origin-header)
+ x-forwarded-host-header (ring.mock/header "X-Forwarded-Host" x-forwarded-host-header)
+ host-header (ring.mock/header "Host" host-header)))
+
(deftest maybe-set-site-url-test
(testing "Make sure `maybe-set-site-url` middleware looks at the correct headers in the correct order (#12528)"
- (let [handler (fn [request respond _]
- (respond request))
- maybe-set-site-url (fn [request]
- ((mw.misc/maybe-set-site-url handler) request identity (fn [e] (throw e))))]
- (doseq [origin-header ["https://mb1.example.com" nil]
- x-forwarded-host-header ["https://mb2.example.com" nil]
- host-header ["https://mb3.example.com" nil]
- :let [request (cond-> (m/dissoc-in (ring.mock/request :get "/") [:headers "host"])
- origin-header (ring.mock/header "Origin" origin-header)
- x-forwarded-host-header (ring.mock/header "X-Forwarded-Host" x-forwarded-host-header)
- host-header (ring.mock/header "Host" host-header))]]
- (testing (format "headers = %s" (pr-str (:headers request)))
- (mt/with-temporary-setting-values [site-url nil]
- (maybe-set-site-url request)
- (is (= (or origin-header x-forwarded-host-header host-header)
- (public-settings/site-url)))))))))
+ (doseq [origin-header ["https://mb1.example.com" nil]
+ x-forwarded-host-header ["https://mb2.example.com" nil]
+ host-header ["https://mb3.example.com" nil]
+ :let [request (mock-request "/" origin-header x-forwarded-host-header host-header)]]
+ (testing (format "headers = %s" (pr-str (:headers request)))
+ (mt/with-temporary-setting-values [site-url nil]
+ (maybe-set-site-url request)
+ (is (= (or origin-header x-forwarded-host-header host-header)
+ (public-settings/site-url)))))))
+ (testing "Site URL should not be inferred from healthcheck requests"
+ (mt/with-temporary-setting-values [site-url nil]
+ (let [request (ring.mock/request :get "/api/health")]
+ (maybe-set-site-url request)
+ (is (nil? (public-settings/site-url))))))
+ (testing "Site URL should not be inferred if already set by env variable"
+ (mt/with-temporary-setting-values [site-url nil]
+ (mt/with-temp-env-var-value [mb-site-url "https://mb1.example.com"]
+ (let [request (mock-request "/" "https://mb2.example.com" nil nil)]
+ (maybe-set-site-url request)
+ (is (= "https://mb1.example.com" (public-settings/site-url))))))))
From 7ad2d6fddfbe4d91367487ab446a637110abd99a Mon Sep 17 00:00:00 2001
From: Alexander Polyankin
Date: Wed, 21 Jul 2021 11:01:05 +0300
Subject: [PATCH 071/664] Do not change route in case collection archiving is
cancelled (#17135)
---
.../metabase/components/ArchiveCollectionModal.jsx | 13 ++++++++-----
.../scenarios/collections/permissions.cy.spec.js | 4 ++--
2 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/frontend/src/metabase/components/ArchiveCollectionModal.jsx b/frontend/src/metabase/components/ArchiveCollectionModal.jsx
index 727bf3cf382f..0d5d2ebaa8ce 100644
--- a/frontend/src/metabase/components/ArchiveCollectionModal.jsx
+++ b/frontend/src/metabase/components/ArchiveCollectionModal.jsx
@@ -35,11 +35,14 @@ class ArchiveCollectionModal extends React.Component {
close = () => {
const { onClose, object, push } = this.props;
onClose();
- const parent =
- object.effective_ancestors.length > 0
- ? object.effective_ancestors.pop()
- : null;
- push(Urls.collection(parent));
+
+ if (object.archived) {
+ const parent =
+ object.effective_ancestors.length > 0
+ ? object.effective_ancestors.pop()
+ : null;
+ push(Urls.collection(parent));
+ }
};
render() {
diff --git a/frontend/test/metabase/scenarios/collections/permissions.cy.spec.js b/frontend/test/metabase/scenarios/collections/permissions.cy.spec.js
index 82254e3c68fd..154e910c1159 100644
--- a/frontend/test/metabase/scenarios/collections/permissions.cy.spec.js
+++ b/frontend/test/metabase/scenarios/collections/permissions.cy.spec.js
@@ -264,7 +264,7 @@ describe("collection permissions", () => {
// });
});
- it.skip("abandoning archive process should keep you in the same collection (metabase#15289)", () => {
+ it("abandoning archive process should keep you in the same collection (metabase#15289)", () => {
cy.request("GET", "/api/collection").then(xhr => {
const { id: THIRD_COLLECTION_ID } = xhr.body.find(
collection => collection.slug === "third_collection",
@@ -277,7 +277,7 @@ describe("collection permissions", () => {
.click();
cy.location("pathname").should(
"eq",
- `/collection/${THIRD_COLLECTION_ID}`,
+ `/collection/${THIRD_COLLECTION_ID}-third-collection`,
);
cy.get("[class*=PageHeading]")
.as("title")
From ca7be424e555e6faed2fa17236ecef9e88857288 Mon Sep 17 00:00:00 2001
From: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Date: Wed, 21 Jul 2021 11:15:28 +0200
Subject: [PATCH 072/664] #17139 Repro: Clicking on cancel edit dashboard
without any changes removes the dashboard filter (#17140)
---
.../e2e/helpers/e2e-dashboard-helpers.js | 23 ++++++++
.../reproductions/17139.cy.spec.js | 56 +++++++++++++++++++
2 files changed, 79 insertions(+)
create mode 100644 frontend/test/metabase/scenarios/dashboard-filters/reproductions/17139.cy.spec.js
diff --git a/frontend/test/__support__/e2e/helpers/e2e-dashboard-helpers.js b/frontend/test/__support__/e2e/helpers/e2e-dashboard-helpers.js
index 4d59621126c4..29f7d8e4d9f0 100644
--- a/frontend/test/__support__/e2e/helpers/e2e-dashboard-helpers.js
+++ b/frontend/test/__support__/e2e/helpers/e2e-dashboard-helpers.js
@@ -13,3 +13,26 @@ export function showDashboardCardActions(index = 0) {
.eq(index)
.realHover();
}
+
+export function editDashboard() {
+ cy.icon("pencil").click();
+}
+
+export function cancelEditingDashboard() {
+ cy.findByText("Cancel").click();
+ cy.findByText("You're editing this dashboard.").should("not.exist");
+}
+
+export function saveDashboard() {
+ cy.findByText("Save").click();
+ cy.findByText("You're editing this dashboard.").should("not.exist");
+}
+
+export function checkFilterLabelAndValue(label, value) {
+ cy.get("fieldset")
+ .find("legend")
+ .invoke("text")
+ .should("eq", label);
+
+ cy.get("fieldset").contains(value);
+}
diff --git a/frontend/test/metabase/scenarios/dashboard-filters/reproductions/17139.cy.spec.js b/frontend/test/metabase/scenarios/dashboard-filters/reproductions/17139.cy.spec.js
new file mode 100644
index 000000000000..4e7c86f647c2
--- /dev/null
+++ b/frontend/test/metabase/scenarios/dashboard-filters/reproductions/17139.cy.spec.js
@@ -0,0 +1,56 @@
+import {
+ restore,
+ popover,
+ mockSessionProperty,
+ filterWidget,
+ editDashboard,
+ cancelEditingDashboard,
+ saveDashboard,
+ checkFilterLabelAndValue,
+} from "__support__/e2e/cypress";
+
+import { setMonthAndYear } from "../../native-filters/helpers/e2e-date-filter-helpers";
+
+describe.skip("issue 17139", () => {
+ beforeEach(() => {
+ restore();
+ cy.signInAsAdmin();
+ mockSessionProperty("field-filter-operators-enabled?", true);
+
+ cy.visit("/dashboard/1");
+
+ editDashboard();
+
+ cy.icon("filter").click();
+ popover().within(() => {
+ cy.findByText("What do you want to filter?");
+ cy.findByText("Time").click();
+ cy.findByText("Month and Year").click();
+ });
+
+ cy.findByText("Select…").click();
+ popover()
+ .contains("Created At")
+ .first()
+ .click();
+
+ saveDashboard();
+ });
+
+ it("should not reset previously defined filters when exiting 'edit' mode without making any changes (metabase#17139)", () => {
+ filterWidget()
+ .contains("Month and Year")
+ .click();
+
+ setMonthAndYear({ month: "November", year: "2016" });
+
+ cy.url().should("contain", "?month_and_year=2016-11");
+ checkFilterLabelAndValue("Month and Year", "November, 2016");
+
+ editDashboard();
+ cancelEditingDashboard();
+
+ cy.url().should("contain", "?month_and_year=2016-11");
+ checkFilterLabelAndValue("Month and Year", "November, 2016");
+ });
+});
From 100406fd255b0567a68aba9e5a09f2f72dfa361d Mon Sep 17 00:00:00 2001
From: Anton Kulyk
Date: Wed, 21 Jul 2021 15:43:44 +0300
Subject: [PATCH 073/664] Add Metabase Cloud link to admin settings for hosted
instances (#17134)
* Add `storeUrl` method to MetabaseSettings
* Use `storeUrl` method
* Add Cloud settings tab
* Add E2E tests for cloud settings page
---
.../SettingsCloudStoreLink.jsx | 17 +++++++++
.../SettingsCloudStoreLink.styled.js | 30 +++++++++++++++
.../SettingsCloudStoreLink/index.js | 1 +
.../components/widgets/EmbeddingLevel.jsx | 6 ++-
.../src/metabase/admin/settings/selectors.js | 17 +++++++++
frontend/src/metabase/lib/settings.js | 4 ++
.../metabase/nav/components/ProfileLink.jsx | 2 +-
.../scenarios/admin/settings/cloud.cy.spec.js | 37 +++++++++++++++++++
8 files changed, 111 insertions(+), 3 deletions(-)
create mode 100644 frontend/src/metabase/admin/settings/components/SettingsCloudStoreLink/SettingsCloudStoreLink.jsx
create mode 100644 frontend/src/metabase/admin/settings/components/SettingsCloudStoreLink/SettingsCloudStoreLink.styled.js
create mode 100644 frontend/src/metabase/admin/settings/components/SettingsCloudStoreLink/index.js
create mode 100644 frontend/test/metabase/scenarios/admin/settings/cloud.cy.spec.js
diff --git a/frontend/src/metabase/admin/settings/components/SettingsCloudStoreLink/SettingsCloudStoreLink.jsx b/frontend/src/metabase/admin/settings/components/SettingsCloudStoreLink/SettingsCloudStoreLink.jsx
new file mode 100644
index 000000000000..5cc2230510c5
--- /dev/null
+++ b/frontend/src/metabase/admin/settings/components/SettingsCloudStoreLink/SettingsCloudStoreLink.jsx
@@ -0,0 +1,17 @@
+import React from "react";
+import { t } from "ttag";
+import MetabaseSettings from "metabase/lib/settings";
+import { Description, Link, LinkIcon } from "./SettingsCloudStoreLink.styled";
+
+export function SettingsCloudStoreLink() {
+ const url = MetabaseSettings.storeUrl();
+ return (
+
+ {t`Manage your Cloud account, including billing preferences and technical settings about this instance in your Metabase Store account.`}
+
+ {t`Go to the Metabase Store`}
+
+
+
+ );
+}
diff --git a/frontend/src/metabase/admin/settings/components/SettingsCloudStoreLink/SettingsCloudStoreLink.styled.js b/frontend/src/metabase/admin/settings/components/SettingsCloudStoreLink/SettingsCloudStoreLink.styled.js
new file mode 100644
index 000000000000..0952a5b9ebd4
--- /dev/null
+++ b/frontend/src/metabase/admin/settings/components/SettingsCloudStoreLink/SettingsCloudStoreLink.styled.js
@@ -0,0 +1,30 @@
+import styled from "styled-components";
+import { color } from "metabase/lib/colors";
+import ExternalLink from "metabase/components/ExternalLink";
+import Icon from "metabase/components/Icon";
+
+export const Description = styled.p`
+ color: ${color("text-dark")};
+ max-width: 360px;
+`;
+
+export const Link = styled(ExternalLink)`
+ display: inline-flex;
+ align-items: center;
+ color: ${color("text-white")};
+ font-weight: bold;
+ background-color: ${color("brand")};
+ padding: 12px 18px;
+ border-radius: 6px;
+
+ &:hover {
+ opacity: 0.88;
+ transition: all 200ms linear;
+ }
+`;
+
+export const LinkIcon = styled(Icon)`
+ color: ${color("text-white")};
+ opacity: 0.6;
+ margin-left: 8px;
+`;
diff --git a/frontend/src/metabase/admin/settings/components/SettingsCloudStoreLink/index.js b/frontend/src/metabase/admin/settings/components/SettingsCloudStoreLink/index.js
new file mode 100644
index 000000000000..6ae42da6388f
--- /dev/null
+++ b/frontend/src/metabase/admin/settings/components/SettingsCloudStoreLink/index.js
@@ -0,0 +1 @@
+export * from "./SettingsCloudStoreLink";
diff --git a/frontend/src/metabase/admin/settings/components/widgets/EmbeddingLevel.jsx b/frontend/src/metabase/admin/settings/components/widgets/EmbeddingLevel.jsx
index be075ba2bd3c..fccae6071fb9 100644
--- a/frontend/src/metabase/admin/settings/components/widgets/EmbeddingLevel.jsx
+++ b/frontend/src/metabase/admin/settings/components/widgets/EmbeddingLevel.jsx
@@ -5,9 +5,11 @@ import SettingsInput from "./SettingInput";
import cx from "classnames";
import ExternalLink from "metabase/components/ExternalLink";
+import MetabaseSettings from "metabase/lib/settings";
-const PREMIUM_EMBEDDING_STORE_URL =
- "https://store.metabase.com/product/embedding";
+const PREMIUM_EMBEDDING_STORE_URL = MetabaseSettings.storeUrl(
+ "product/embedding",
+);
const PREMIUM_EMBEDDING_SETTING_KEY = "premium-embedding-token";
class PremiumTokenInput extends Component {
diff --git a/frontend/src/metabase/admin/settings/selectors.js b/frontend/src/metabase/admin/settings/selectors.js
index 428434ce2625..1304b1f61a33 100644
--- a/frontend/src/metabase/admin/settings/selectors.js
+++ b/frontend/src/metabase/admin/settings/selectors.js
@@ -16,6 +16,7 @@ import EmbeddingLegalese from "./components/widgets/EmbeddingLegalese";
import EmbeddingLevel from "./components/widgets/EmbeddingLevel";
import FormattingWidget from "./components/widgets/FormattingWidget";
+import { SettingsCloudStoreLink } from "./components/SettingsCloudStoreLink";
import SettingsUpdatesForm from "./components/SettingsUpdatesForm/SettingsUpdatesForm";
import SettingsEmailForm from "./components/SettingsEmailForm";
import SettingsSetupList from "./components/SettingsSetupList";
@@ -392,6 +393,22 @@ const SECTIONS = updateSectionsWithPlugins({
},
});
+if (MetabaseSettings.isHosted()) {
+ const allSections = Object.values(SECTIONS);
+ const lastSection = _.max(allSections, "order");
+ SECTIONS.cloud = {
+ name: t`Cloud`,
+ order: lastSection.order + 1,
+ settings: [
+ {
+ key: "store-link",
+ display_name: t`Cloud Settings`,
+ widget: SettingsCloudStoreLink,
+ },
+ ],
+ };
+}
+
export const getSettings = createSelector(
state => state.admin.settings.settings,
state => state.admin.settings.warnings,
diff --git a/frontend/src/metabase/lib/settings.js b/frontend/src/metabase/lib/settings.js
index 2e5315f184bd..0433689373b9 100644
--- a/frontend/src/metabase/lib/settings.js
+++ b/frontend/src/metabase/lib/settings.js
@@ -157,6 +157,10 @@ class Settings {
return `https://www.metabase.com/docs/${tag}/${page}${anchor}`;
}
+ storeUrl(path = "") {
+ return `https://store.metabase.com/${path}`;
+ }
+
newVersionAvailable() {
const result = MetabaseUtils.compareVersions(
this.currentVersion(),
diff --git a/frontend/src/metabase/nav/components/ProfileLink.jsx b/frontend/src/metabase/nav/components/ProfileLink.jsx
index f99982eb5468..e2370bea3961 100644
--- a/frontend/src/metabase/nav/components/ProfileLink.jsx
+++ b/frontend/src/metabase/nav/components/ProfileLink.jsx
@@ -49,7 +49,7 @@ export default class ProfileLink extends Component {
admin && [
{
title: t`Manage Metabase Cloud`,
- link: "https://store.metabase.com/login",
+ link: MetabaseSettings.storeUrl("login"),
event: `Navbar;Profile Dropdown;ManageHosting ${tag}`,
externalLink: true,
},
diff --git a/frontend/test/metabase/scenarios/admin/settings/cloud.cy.spec.js b/frontend/test/metabase/scenarios/admin/settings/cloud.cy.spec.js
new file mode 100644
index 000000000000..411432b34e3e
--- /dev/null
+++ b/frontend/test/metabase/scenarios/admin/settings/cloud.cy.spec.js
@@ -0,0 +1,37 @@
+import { restore } from "__support__/e2e/cypress";
+
+describe("Cloud settings section", () => {
+ beforeEach(() => {
+ restore();
+ cy.signInAsAdmin();
+ });
+
+ it("should be visible when running Metabase Cloud", () => {
+ setupMetabaseCloud();
+ cy.visit("/admin");
+ cy.get(".AdminList-items")
+ .findByText("Cloud")
+ .click();
+ cy.findByText(/Cloud Settings/i);
+ cy.findByText("Go to the Metabase Store").should(
+ "have.attr",
+ "href",
+ "https://store.metabase.com/",
+ );
+ });
+
+ it("should be invisible when self-hosting", () => {
+ cy.visit("/admin");
+ cy.get(".AdminList-items")
+ .findByText("Cloud")
+ .should("not.exist");
+ cy.visit("/admin/settings/cloud");
+ cy.findByText(/Cloud Settings/i).should("not.exist");
+ });
+});
+
+function setupMetabaseCloud() {
+ cy.request("PUT", "/api/setting/site-url", {
+ value: "https://CYPRESSTESTENVIRONMENT.metabaseapp.com",
+ });
+}
From 8d644391eeb2af656d68bd391cf7fc27d629fedf Mon Sep 17 00:00:00 2001
From: Anton Kulyk
Date: Wed, 21 Jul 2021 16:14:20 +0300
Subject: [PATCH 074/664] Fix dashboard card hovering buttons drag behavior
(#17130)
---
frontend/src/metabase/dashboard/components/DashCard.jsx | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/frontend/src/metabase/dashboard/components/DashCard.jsx b/frontend/src/metabase/dashboard/components/DashCard.jsx
index 54aeb15adec8..f3117d730529 100644
--- a/frontend/src/metabase/dashboard/components/DashCard.jsx
+++ b/frontend/src/metabase/dashboard/components/DashCard.jsx
@@ -92,6 +92,10 @@ export default class DashCard extends Component {
}));
};
+ preventDragging = e => {
+ e.stopPropagation();
+ };
+
render() {
const {
dashcard,
@@ -177,7 +181,7 @@ export default class DashCard extends Component {
}
>
{isEditingDashboardLayout ? (
-
+
Date: Wed, 21 Jul 2021 08:22:00 -0700
Subject: [PATCH 075/664] site-url test improvements (#17142)
---
test/metabase/server/middleware/misc_test.clj | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/test/metabase/server/middleware/misc_test.clj b/test/metabase/server/middleware/misc_test.clj
index f491fe544ac2..4698269c5ed5 100644
--- a/test/metabase/server/middleware/misc_test.clj
+++ b/test/metabase/server/middleware/misc_test.clj
@@ -33,9 +33,14 @@
(public-settings/site-url)))))))
(testing "Site URL should not be inferred from healthcheck requests"
(mt/with-temporary-setting-values [site-url nil]
- (let [request (ring.mock/request :get "/api/health")]
+ (let [request (mock-request "/api/health" "https://mb1.example.com" nil nil)]
(maybe-set-site-url request)
(is (nil? (public-settings/site-url))))))
+ (testing "Site URL should not be inferred if already set in DB"
+ (mt/with-temporary-setting-values [site-url "https://mb1.example.com"]
+ (let [request (mock-request "/" "https://mb2.example.com" nil nil)]
+ (maybe-set-site-url request)
+ (is (= "https://mb1.example.com" (public-settings/site-url))))))
(testing "Site URL should not be inferred if already set by env variable"
(mt/with-temporary-setting-values [site-url nil]
(mt/with-temp-env-var-value [mb-site-url "https://mb1.example.com"]
From 1127578af2250943ef9e11559228a725f988571a Mon Sep 17 00:00:00 2001
From: Gustavo Saiani
Date: Wed, 21 Jul 2021 12:28:36 -0300
Subject: [PATCH 076/664] Extract component from ParameterFieldWidget (#17091)
---
.../components/ParameterValueWidget.jsx | 2 +-
.../ParameterFieldWidget.jsx | 32 +++++------------
.../ParameterFieldWidgetValue.jsx | 35 +++++++++++++++++++
.../ParameterFieldWidgetValue.unit.spec.js | 20 +++++++++++
.../ParameterFieldWidget/normalizeValue.js | 7 ++++
.../normalizeValue.unit.spec.js | 26 ++++++++++++++
6 files changed, 97 insertions(+), 25 deletions(-)
rename frontend/src/metabase/parameters/components/widgets/{ => ParameterFieldWidget}/ParameterFieldWidget.jsx (88%)
create mode 100644 frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/ParameterFieldWidgetValue/ParameterFieldWidgetValue.jsx
create mode 100644 frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/ParameterFieldWidgetValue/ParameterFieldWidgetValue.unit.spec.js
create mode 100644 frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/normalizeValue.js
create mode 100644 frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/normalizeValue.unit.spec.js
diff --git a/frontend/src/metabase/parameters/components/ParameterValueWidget.jsx b/frontend/src/metabase/parameters/components/ParameterValueWidget.jsx
index 5e04bc8e44cb..40cb4183c128 100644
--- a/frontend/src/metabase/parameters/components/ParameterValueWidget.jsx
+++ b/frontend/src/metabase/parameters/components/ParameterValueWidget.jsx
@@ -14,7 +14,7 @@ import DateMonthYearWidget from "./widgets/DateMonthYearWidget";
import DateQuarterYearWidget from "./widgets/DateQuarterYearWidget";
import DateAllOptionsWidget from "./widgets/DateAllOptionsWidget";
import TextWidget from "./widgets/TextWidget";
-import ParameterFieldWidget from "./widgets/ParameterFieldWidget";
+import ParameterFieldWidget from "./widgets/ParameterFieldWidget/ParameterFieldWidget";
import { fetchField, fetchFieldValues } from "metabase/redux/metadata";
import {
diff --git a/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget.jsx b/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/ParameterFieldWidget.jsx
similarity index 88%
rename from frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget.jsx
rename to frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/ParameterFieldWidget.jsx
index b44dde8b9036..1fb069a0a220 100644
--- a/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget.jsx
+++ b/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/ParameterFieldWidget.jsx
@@ -2,13 +2,15 @@ import React, { Component } from "react";
import ReactDOM from "react-dom";
import PropTypes from "prop-types";
-import { t, ngettext, msgid } from "ttag";
+import { t } from "ttag";
import _ from "underscore";
import FieldValuesWidget from "metabase/components/FieldValuesWidget";
+import ParameterFieldWidgetValue from "./ParameterFieldWidgetValue/ParameterFieldWidgetValue";
import Popover from "metabase/components/Popover";
import Button from "metabase/components/Button";
-import Value from "metabase/components/Value";
+
+import { normalizeValue } from "./normalizeValue";
import cx from "classnames";
import {
@@ -32,9 +34,6 @@ const propTypes = {
const BORDER_WIDTH = 1;
-const normalizeValue = value =>
- Array.isArray(value) ? value : value != null ? [value] : [];
-
export default class ParameterFieldWidget extends Component {
constructor(props) {
super(props);
@@ -47,24 +46,6 @@ export default class ParameterFieldWidget extends Component {
static noPopover = true;
- static format(value, fields) {
- value = normalizeValue(value);
- if (value.length > 1) {
- const n = value.length;
- return ngettext(msgid`${n} selection`, `${n} selections`, n);
- } else {
- return (
-
- );
- }
- }
-
UNSAFE_componentWillReceiveProps(nextProps) {
if (this.props.value !== nextProps.value) {
this.setState({ value: nextProps.value });
@@ -126,7 +107,10 @@ export default class ParameterFieldWidget extends Component {
onClick={() => focusChanged(true)}
>
{savedValue.length > 0 ? (
- ParameterFieldWidget.format(savedValue, fields)
+
) : (
{placeholder}
)}
diff --git a/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/ParameterFieldWidgetValue/ParameterFieldWidgetValue.jsx b/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/ParameterFieldWidgetValue/ParameterFieldWidgetValue.jsx
new file mode 100644
index 000000000000..5e566e567d2d
--- /dev/null
+++ b/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/ParameterFieldWidgetValue/ParameterFieldWidgetValue.jsx
@@ -0,0 +1,35 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { ngettext, msgid } from "ttag";
+
+import Value from "metabase/components/Value";
+import { normalizeValue } from "../normalizeValue";
+
+function renderNumberOfSelections(numberOfSelections) {
+ return ngettext(
+ msgid`${numberOfSelections} selection`,
+ `${numberOfSelections} selections`,
+ numberOfSelections,
+ );
+}
+
+export default function ParameterFieldWidgetValue({ savedValue, fields }) {
+ const values = normalizeValue(savedValue);
+
+ const numberOfValues = values.length;
+
+ // If there are multiple fields, turn off remapping since they might
+ // be remapped to different fields.
+ const shouldRemap = fields.length === 1;
+
+ return numberOfValues > 1 ? (
+ renderNumberOfSelections(numberOfValues)
+ ) : (
+
+ );
+}
+
+ParameterFieldWidgetValue.propTypes = {
+ savedValue: PropTypes.array,
+ fields: PropTypes.array,
+};
diff --git a/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/ParameterFieldWidgetValue/ParameterFieldWidgetValue.unit.spec.js b/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/ParameterFieldWidgetValue/ParameterFieldWidgetValue.unit.spec.js
new file mode 100644
index 000000000000..d6e802eb5156
--- /dev/null
+++ b/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/ParameterFieldWidgetValue/ParameterFieldWidgetValue.unit.spec.js
@@ -0,0 +1,20 @@
+import React from "react";
+
+import ParameterFieldWidgetValue from "./ParameterFieldWidgetValue";
+import { render, screen } from "@testing-library/react";
+
+const value = "A value";
+
+describe("when fields is empty array", () => {
+ it("renders savedValue if it is a single item", () => {
+ render();
+ screen.getByText(value);
+ });
+
+ it("renders number of selections if multiple items", () => {
+ render(
+ ,
+ );
+ screen.getByText("2 selections");
+ });
+});
diff --git a/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/normalizeValue.js b/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/normalizeValue.js
new file mode 100644
index 000000000000..f83230f4451e
--- /dev/null
+++ b/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/normalizeValue.js
@@ -0,0 +1,7 @@
+export function normalizeValue(value) {
+ if (Array.isArray(value)) {
+ return value;
+ }
+
+ return value ? [value] : [];
+}
diff --git a/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/normalizeValue.unit.spec.js b/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/normalizeValue.unit.spec.js
new file mode 100644
index 000000000000..72386cb63912
--- /dev/null
+++ b/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/normalizeValue.unit.spec.js
@@ -0,0 +1,26 @@
+import { normalizeValue } from "./normalizeValue";
+
+it("returns empty array if value is null", () => {
+ const value = null;
+ const expected = [];
+
+ const normalized = normalizeValue(value);
+
+ expect(normalized).toEqual(expected);
+});
+
+it("returns value if value is an array", () => {
+ const value = [1];
+
+ const normalized = normalizeValue(value);
+
+ expect(normalized).toBe(value);
+});
+
+it("returns value as item of array if passed value is not an array", () => {
+ const value = 1;
+
+ const normalized = normalizeValue(value);
+
+ expect(normalized).toEqual([value]);
+});
From 3462edcd661d1e31b121640636568813d3d36858 Mon Sep 17 00:00:00 2001
From: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Date: Thu, 22 Jul 2021 00:41:32 +0200
Subject: [PATCH 077/664] Remove `image diff` (#17159)
* Remove `image-diff` library
* Remove `compare-screenshots` script
---
bin/compare-screenshots | 145 ----------------------------------------
package.json | 1 -
yarn.lock | 99 +--------------------------
3 files changed, 1 insertion(+), 244 deletions(-)
delete mode 100755 bin/compare-screenshots
diff --git a/bin/compare-screenshots b/bin/compare-screenshots
deleted file mode 100755
index 4f7e2535a482..000000000000
--- a/bin/compare-screenshots
+++ /dev/null
@@ -1,145 +0,0 @@
-#!/usr/bin/env babel-node
-
-import fetch from "isomorphic-fetch";
-import path from "path";
-import fs from "fs-promise"
-import imageDiff_ from "image-diff";
-import https from "https";
-import os from "os";
-
-import { WebClient } from "@slack/client";
-
-const CIRCLECI_TOKEN = process.env["CIRCLECI_TOKEN"];
-const SLACK_TOKEN = process.env["SLACK_TOKEN"];
-const SLACK_CHANNEL = "#ci-screenshots";
-
-const CIRCLE_PROJECT = "github/metabase/metabase";
-const CIRCLE_BRANCH = "master";
-
-const CIRCLE_SCREENSHOT_PATH = "/home/ubuntu/metabase/screenshots/";
-
-const slack = new WebClient(SLACK_TOKEN);
-
-async function circleci(path) {
- const response = await fetch(
- `https://circleci.com/api/v1.1/${path}?circle-token=${encodeURIComponent(CIRCLECI_TOKEN)}`
- );
- return response.json();
-}
-
-function imageDiff(options) {
- return new Promise((resolve, reject) => {
- imageDiff_.getFullResult(options, (err, result) =>
- err ? reject(err) : resolve(result)
- );
- });
-}
-
-function download(url, path) {
- return new Promise((resolve, reject) => {
- https.get(url, response => {
- response.pipe(fs.createWriteStream(path)).on("finish", resolve);
- }).on('error', reject);
- });
-}
-
-async function getCircleArtifactScreenshots(buildPath) {
- let artifacts = await circleci(`project/${buildPath}/artifacts`);
- let results = {};
- for (const artifact of artifacts) {
- if (artifact.pretty_path.startsWith(CIRCLE_SCREENSHOT_PATH)) {
- const downloadPath = path.join(os.tmpdir(), path.basename(artifact.pretty_path));
- console.log("Downloading ", artifact.url, "to", downloadPath);
- await download(artifact.url, downloadPath);
- results[artifact.pretty_path.slice(CIRCLE_SCREENSHOT_PATH.length)] = downloadPath;
- }
- }
- return results;
-}
-
-async function getLocalScreenshots(directory) {
- const filenames = await fs.readdir(directory);
- let results = {};
- for (const filename of filenames) {
- results[filename] = path.resolve(directory, filename);
- }
- return results;
-}
-
-async function getScreenshots(target) {
- if (target.circleProject && target.circleBranch) {
- let builds = await circleci(`project/${target.circleProject}/tree/${target.circleBranch}`);
- let ok = builds.filter(build => build.status === "success" || build.status === "fixed");
- let screenshots = await getCircleArtifactScreenshots(`${target.circleProject}/${ok[0].build_num}`);
- return screenshots;
- } else if (target.localDirectory) {
- return await getLocalScreenshots(target.localDirectory);
- } else {
- throw "unknown target type";
- }
-}
-
-async function run(expectedTarget, actualTarget) {
- try {
- const expectedScreenshots = await getScreenshots(expectedTarget);
- console.log("Expected", Object.keys(expectedScreenshots));
- const actualScreenshots = await getScreenshots(actualTarget);
- console.log("Actual", Object.keys(expectedScreenshots));
- let images = Object.keys({ ...expectedScreenshots, ...actualScreenshots });
-
- for (const image of images) {
- const expectedImage = expectedScreenshots[image];
- const actualImage = actualScreenshots[image];
- const diffImage = path.join(os.tmpdir(), "diff-"+image);
- if (!actualImage) {
- console.log("Added", image);
- await slack.files.upload(image, {
- title: "Added " + image,
- channels: [SLACK_CHANNEL],
- file: fs.createReadStream(actualImage)
- });
- } else if (!expectedImage) {
- console.log("Removed", image);
- await slack.files.upload(image, {
- title: "Removed " + image,
- channels: [SLACK_CHANNEL],
- file: fs.createReadStream(expectedImage)
- });
- } else {
- const result = await imageDiff({
- expectedImage,
- actualImage,
- diffImage,
- shadow: true
- })
- if (result.percentage === 0.0) {
- console.log("No difference", image);
- } else {
- console.log("Changed", result.percentage.toFixed(2), image);
- await slack.files.upload(image, {
- title: "Diff (" + result.percentage.toFixed(2) + ") " + image,
- channels: [SLACK_CHANNEL],
- file: fs.createReadStream(diffImage)
- });
- await slack.files.upload(image, {
- title: "Expected " + image,
- channels: [SLACK_CHANNEL],
- file: fs.createReadStream(expectedImage)
- });
- await slack.files.upload(image, {
- title: "Actual " + image,
- channels: [SLACK_CHANNEL],
- file: fs.createReadStream(actualImage)
- });
- }
- }
- }
- } catch (e) {
- console.error(e);
- }
-}
-
-run(
- { circleProject: CIRCLE_PROJECT, circleBranch: CIRCLE_BRANCH },
- { localDirectory: "screenshots" }
-);
diff --git a/package.json b/package.json
index 1e2538205f55..1da6d1cd4019 100644
--- a/package.json
+++ b/package.json
@@ -132,7 +132,6 @@
"html-webpack-harddisk-plugin": "^2.0.0",
"html-webpack-plugin": "^5.3.1",
"husky": "^6.0.0",
- "image-diff": "^1.6.3",
"jest": "^19.0.2",
"jest-localstorage-mock": "^2.2.0",
"jsonwebtoken": "^7.2.1",
diff --git a/yarn.lock b/yarn.lock
index 23b2a7b65a03..c35525d9bd96 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1802,16 +1802,6 @@ array-includes@^3.1.2:
get-intrinsic "^1.1.1"
is-string "^1.0.5"
-array-parallel@~0.1.3:
- version "0.1.3"
- resolved "https://registry.yarnpkg.com/array-parallel/-/array-parallel-0.1.3.tgz#8f785308926ed5aa478c47e64d1b334b6c0c947d"
- integrity sha1-j3hTCJJu1apHjEfmTRszS2wMlH0=
-
-array-series@~0.1.5:
- version "0.1.5"
- resolved "https://registry.yarnpkg.com/array-series/-/array-series-0.1.5.tgz#df5d37bfc5c2ef0755e2aa4f92feae7d4b5a972f"
- integrity sha1-3103v8XC7wdV4qpPkv6ufUtaly8=
-
array-union@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
@@ -1954,11 +1944,6 @@ async@^3.2.0:
resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
-async@~0.2.9:
- version "0.2.10"
- resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
- integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E=
-
async@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9"
@@ -3315,15 +3300,6 @@ buffer@^6.0.3:
base64-js "^1.3.1"
ieee754 "^1.2.1"
-buffered-spawn@~1.1.1:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/buffered-spawn/-/buffered-spawn-1.1.2.tgz#21ad9735dfbf6576745be0d74a23ef257bf3c58d"
- integrity sha1-Ia2XNd+/ZXZ0W+DXSiPvJXvzxY0=
- dependencies:
- cross-spawn-async "^1.0.1"
- err-code "^0.1.0"
- q "^1.0.1"
-
builtin-status-codes@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
@@ -3945,13 +3921,6 @@ commander@~2.17.1:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
-commander@~2.9.0:
- version "2.9.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
- integrity sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=
- dependencies:
- graceful-readlink ">= 1.0.0"
-
common-tags@^1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
@@ -4227,14 +4196,6 @@ create-react-class@^15.5.1, create-react-class@^15.5.2:
loose-envify "^1.3.1"
object-assign "^4.1.1"
-cross-spawn-async@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/cross-spawn-async/-/cross-spawn-async-1.0.1.tgz#bb525c1e420d9942552e04791a3eb2d9887a105f"
- integrity sha1-u1JcHkINmUJVLgR5Gj6y2Yh6EF8=
- dependencies:
- lru-cache "^2.6.5"
- which "^1.1.1"
-
cross-spawn@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
@@ -4637,13 +4598,6 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
dependencies:
ms "2.1.2"
-debug@~2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
- integrity sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=
- dependencies:
- ms "0.7.1"
-
decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@@ -5185,11 +5139,6 @@ envinfo@^7.7.3:
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475"
integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==
-err-code@^0.1.0:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/err-code/-/err-code-0.1.2.tgz#122a92b3342b9899da02b5ac994d30f95d4763ee"
- integrity sha1-EiqSszQrmJnaArWsmU0w+V1HY+4=
-
errno@^0.1.3, errno@~0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
@@ -6433,15 +6382,6 @@ globby@^6.1.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
-gm@~1.21.1:
- version "1.21.1"
- resolved "https://registry.yarnpkg.com/gm/-/gm-1.21.1.tgz#7ed5ed05db36d30c1943f39c3bc1c839b8f2361d"
- integrity sha1-ftXtBds20wwZQ/OcO8HIObjyNh0=
- dependencies:
- array-parallel "~0.1.3"
- array-series "~0.1.5"
- debug "~2.2.0"
-
graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.4"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
@@ -6452,11 +6392,6 @@ graceful-fs@^4.2.4:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
-"graceful-readlink@>= 1.0.0":
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
- integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
-
grid-styled@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/grid-styled/-/grid-styled-4.1.0.tgz#adb060021b9562e02750ce515dae47f03559b4b9"
@@ -6968,18 +6903,6 @@ ignore@^4.0.6:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
-image-diff@^1.6.3:
- version "1.6.3"
- resolved "https://registry.yarnpkg.com/image-diff/-/image-diff-1.6.3.tgz#818a0e656ae89480e802e7ef14db460826f730fc"
- integrity sha1-gYoOZWrolIDoAufvFNtGCCb3MPw=
- dependencies:
- async "~0.2.9"
- buffered-spawn "~1.1.1"
- commander "~2.9.0"
- gm "~1.21.1"
- mkdirp "~0.3.5"
- tmp "0.0.23"
-
import-cwd@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
@@ -8820,11 +8743,6 @@ lower-case@^2.0.2:
dependencies:
tslib "^2.0.3"
-lru-cache@^2.6.5:
- version "2.7.3"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952"
- integrity sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=
-
lru-cache@^4.0.1:
version "4.1.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
@@ -9218,11 +9136,6 @@ mkdirp@^1.0.4:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
-mkdirp@~0.3.5:
- version "0.3.5"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.5.tgz#de3e5f8961c88c787ee1368df849ac4413eca8d7"
- integrity sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=
-
mockdate@^2.0.2:
version "2.0.5"
resolved "https://registry.yarnpkg.com/mockdate/-/mockdate-2.0.5.tgz#70c6abf9ed4b2dae65c81dfc170dd1a5cec53620"
@@ -9265,11 +9178,6 @@ moment@2.x.x, "moment@>= 2.9.0", moment@^2.29.1:
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
-ms@0.7.1:
- version "0.7.1"
- resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
- integrity sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=
-
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -11110,7 +11018,7 @@ punycode@^2.1.0, punycode@^2.1.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
-q@^1.0.1, q@^1.1.2:
+q@^1.1.2:
version "1.5.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
@@ -13466,11 +13374,6 @@ tinycolor2@^1.4.1:
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803"
integrity sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==
-tmp@0.0.23:
- version "0.0.23"
- resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.23.tgz#de874aa5e974a85f0a32cdfdbd74663cb3bd9c74"
- integrity sha1-3odKpel0qF8KMs39vXRmPLO9nHQ=
-
tmp@~0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14"
From a2dd1cf45e4c6db4de2ac0ff1aeffa54a1f32759 Mon Sep 17 00:00:00 2001
From: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Date: Thu, 22 Jul 2021 13:23:21 +0200
Subject: [PATCH 078/664] Add and enable `cypress-grep` library (#16930)
* Add `cypress-grep` library
https://github.com/cypress-io/cypress-grep
* Register `cypress-grep` plugin
---
frontend/test/__support__/e2e/cypress-plugins.js | 1 +
frontend/test/__support__/e2e/cypress.js | 2 ++
package.json | 1 +
yarn.lock | 14 ++++++++++++++
4 files changed, 18 insertions(+)
diff --git a/frontend/test/__support__/e2e/cypress-plugins.js b/frontend/test/__support__/e2e/cypress-plugins.js
index 08147f1b479b..87013d5aa6b9 100644
--- a/frontend/test/__support__/e2e/cypress-plugins.js
+++ b/frontend/test/__support__/e2e/cypress-plugins.js
@@ -32,6 +32,7 @@ const webpackPluginOptions = {
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
+ require("cypress-grep/src/plugin")(config);
/********************************************************************
** WEBPACK **
diff --git a/frontend/test/__support__/e2e/cypress.js b/frontend/test/__support__/e2e/cypress.js
index 774b673abac8..2246c4bea0e2 100644
--- a/frontend/test/__support__/e2e/cypress.js
+++ b/frontend/test/__support__/e2e/cypress.js
@@ -1,3 +1,5 @@
+require("cypress-grep")();
+
import "@testing-library/cypress/add-commands";
import "cypress-real-events/support";
import "@cypress/skip-test/support";
diff --git a/package.json b/package.json
index 1da6d1cd4019..09c29bf9404f 100644
--- a/package.json
+++ b/package.json
@@ -118,6 +118,7 @@
"concurrently": "^3.1.0",
"css-loader": "^0.28.7",
"cypress": "^6.8.0",
+ "cypress-grep": "^2.5.2",
"cypress-real-events": "^1.4.0",
"documentation": "^4.0.0-rc.1",
"eslint": "7.12.1",
diff --git a/yarn.lock b/yarn.lock
index c35525d9bd96..b442edf93717 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4424,6 +4424,13 @@ cycle@1.0.x:
resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2"
integrity sha1-IegLK+hYD5i0aPN5QwZisEbDStI=
+cypress-grep@^2.5.2:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/cypress-grep/-/cypress-grep-2.5.2.tgz#c618014f5c88be7820b5df05a5a286e4a380a040"
+ integrity sha512-Xq7TA8XTzFGPoijmLETyfEqlahfX5WjiatW3IKf6pM+S6ffLvlg669wCblotDVcvnF3uOWuDDeIukRkx6F8smw==
+ dependencies:
+ debug "4.3.1"
+
cypress-real-events@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/cypress-real-events/-/cypress-real-events-1.4.0.tgz#39575031a4020581e0bbf105d7a306ee57d94f48"
@@ -4570,6 +4577,13 @@ debug@4.1.1:
dependencies:
ms "^2.1.1"
+debug@4.3.1:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
+ integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
+ dependencies:
+ ms "2.1.2"
+
debug@4.3.2, debug@^4.0.0:
version "4.3.2"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
From f0ab4de74345b11378aabd5b42fc95e20c27295b Mon Sep 17 00:00:00 2001
From: Alexander Polyankin
Date: Thu, 22 Jul 2021 15:19:06 +0300
Subject: [PATCH 079/664] Fix waterfall visualization with ordinal and numeric
X-axis (#17150)
---
frontend/src/metabase/lib/constants.js | 5 +++
.../metabase/visualizations/lib/apply_axis.js | 18 ++++++--
.../visualizations/lib/renderer_utils.js | 12 ++++--
.../visualizations/waterfall.cy.spec.js | 41 ++++++++++++++++---
4 files changed, 62 insertions(+), 14 deletions(-)
diff --git a/frontend/src/metabase/lib/constants.js b/frontend/src/metabase/lib/constants.js
index da31399c6610..d9b4d5e016bb 100644
--- a/frontend/src/metabase/lib/constants.js
+++ b/frontend/src/metabase/lib/constants.js
@@ -8,4 +8,9 @@ export const NULL_NUMERIC_VALUE = -Infinity;
export const NULL_DISPLAY_VALUE = t`(empty)`;
+// Hack to work with numeric and string x values in waterfall charts
+// Must be a unique string which can be converted to a number since
+// crossfilter converts strings to numbers when grouping starts with numeric data
+export const TOTAL_ORDINAL_VALUE = "Infinity";
+
export const SAVED_QUESTIONS_VIRTUAL_DB_ID = -1337;
diff --git a/frontend/src/metabase/visualizations/lib/apply_axis.js b/frontend/src/metabase/visualizations/lib/apply_axis.js
index 7b50cfaa4ef0..907be19e2ec3 100644
--- a/frontend/src/metabase/visualizations/lib/apply_axis.js
+++ b/frontend/src/metabase/visualizations/lib/apply_axis.js
@@ -301,6 +301,12 @@ export function applyChartOrdinalXAxis(
const dimensionColumn = firstSeries.data.cols[0];
+ const waterfallTotalX =
+ firstSeries.card.display === "waterfall" &&
+ chart.settings["waterfall.show_total"]
+ ? xValues[xValues.length - 1]
+ : null;
+
if (chart.settings["graph.x_axis.labels_enabled"]) {
chart.xAxisLabel(
chart.settings["graph.x_axis.title_text"] ||
@@ -315,14 +321,18 @@ export function applyChartOrdinalXAxis(
chart.xAxis().ticks(xValues.length);
adjustXAxisTicksIfNeeded(chart.xAxis(), chart.width(), xValues);
- chart.xAxis().tickFormat(d =>
- formatValue(d, {
+ chart.xAxis().tickFormat(d => {
+ if (waterfallTotalX && waterfallTotalX === d) {
+ return t`Total`;
+ }
+
+ return formatValue(d, {
...chart.settings.column(dimensionColumn),
type: "axis",
compact: chart.settings["graph.x_axis.labels_enabled"] === "compact",
noRange: isHistogramBar,
- }),
- );
+ });
+ });
} else {
chart.xAxis().ticks(0);
chart.xAxis().tickFormat("");
diff --git a/frontend/src/metabase/visualizations/lib/renderer_utils.js b/frontend/src/metabase/visualizations/lib/renderer_utils.js
index 6a0f790619f7..3c570ff1afe1 100644
--- a/frontend/src/metabase/visualizations/lib/renderer_utils.js
+++ b/frontend/src/metabase/visualizations/lib/renderer_utils.js
@@ -2,11 +2,14 @@
import _ from "underscore";
import { getIn } from "icepick";
-import { t } from "ttag";
import { datasetContainsNoResults } from "metabase/lib/dataset";
import { parseTimestamp } from "metabase/lib/time";
-import { NULL_DISPLAY_VALUE, NULL_NUMERIC_VALUE } from "metabase/lib/constants";
+import {
+ NULL_DISPLAY_VALUE,
+ NULL_NUMERIC_VALUE,
+ TOTAL_ORDINAL_VALUE,
+} from "metabase/lib/constants";
import {
computeTimeseriesDataInverval,
@@ -346,10 +349,11 @@ export function xValueForWaterfallTotal({ settings, series }) {
const lastXValue = xValues[xValues.length - 1];
return lastXValue.clone().add(count, interval);
} else if (isQuantitative(settings) || isHistogram(settings)) {
- return xValues[xValues.length - 1] + xInterval;
+ const maxXValue = _.max(xValues);
+ return maxXValue + xInterval;
}
- return t`Total`;
+ return TOTAL_ORDINAL_VALUE;
}
/************************************************************ PROPERTIES ************************************************************/
diff --git a/frontend/test/metabase/scenarios/visualizations/waterfall.cy.spec.js b/frontend/test/metabase/scenarios/visualizations/waterfall.cy.spec.js
index 80a709ad22eb..954d8c915221 100644
--- a/frontend/test/metabase/scenarios/visualizations/waterfall.cy.spec.js
+++ b/frontend/test/metabase/scenarios/visualizations/waterfall.cy.spec.js
@@ -50,9 +50,38 @@ describe("scenarios > visualizations > waterfall", () => {
verifyWaterfallRendering("PRODUCT", "PROFIT");
});
+ it("should work with ordinal series and numeric X-axis (metabase#15550)", () => {
+ openNativeEditor().type(
+ "select 1 as X, 20 as Y union select 2 as X, -10 as Y",
+ );
+
+ cy.get(".NativeQueryEditor .Icon-play").click();
+ cy.contains("Visualization").click();
+ cy.icon("waterfall").click();
+
+ cy.contains("Select a field").click();
+ cy.get(".List-item")
+ .contains("X")
+ .click();
+
+ cy.contains("Select a field").click();
+ cy.get(".List-item")
+ .contains("Y")
+ .click();
+
+ cy.contains("Axes").click();
+
+ cy.contains("Linear").click();
+ cy.get(".List-item")
+ .contains("Ordinal")
+ .click();
+
+ verifyWaterfallRendering("X", "Y");
+ });
+
it("should work with quantitative series", () => {
openNativeEditor().type(
- "select 1 as xx, 10 as yy union select 2 as xx, -2 as yy",
+ "select 1 as X, 10 as Y union select 2 as X, -2 as Y",
);
cy.get(".NativeQueryEditor .Icon-play").click();
cy.contains("Visualization").click();
@@ -60,14 +89,14 @@ describe("scenarios > visualizations > waterfall", () => {
cy.contains("Select a field").click();
cy.get(".List-item")
- .first()
- .click(); // X
+ .contains("X")
+ .click();
cy.contains("Select a field").click();
cy.get(".List-item")
- .last()
- .click(); // Y
+ .contains("Y")
+ .click();
- verifyWaterfallRendering("XX", "YY");
+ verifyWaterfallRendering("X", "Y");
});
it("should work with time-series data", () => {
From 7ec6f154a3de24f12df6294c45d18568a5faffe3 Mon Sep 17 00:00:00 2001
From: Ariya Hidayat
Date: Thu, 22 Jul 2021 07:39:39 -0700
Subject: [PATCH 080/664] Bundle static viz charting code (#17157)
* yarn add @visx/axis@1.8.0 @visx/grid@1.16.0 @visx/group@1.7.0 @visx/scale@1.7.0 @visx/shape@1.8.0
* Import static viz charting code
* Format code with yarn prettier
* Silence linting violations
* Build static-viz bundle
---
bin/build-mb/src/build.clj | 8 +
.../metabase/static-viz/categorical/donut.js | 64 ++++
.../metabase/static-viz/categorical/index.js | 3 +
frontend/src/metabase/static-viz/index.js | 48 +++
.../src/metabase/static-viz/timeseries/bar.js | 82 ++++++
.../metabase/static-viz/timeseries/index.js | 4 +
.../metabase/static-viz/timeseries/line.js | 88 ++++++
frontend/src/metabase/static-viz/utils.js | 17 ++
package.json | 6 +
webpack.static-viz.config.js | 56 ++++
yarn.lock | 276 +++++++++++++++++-
11 files changed, 647 insertions(+), 5 deletions(-)
create mode 100644 frontend/src/metabase/static-viz/categorical/donut.js
create mode 100644 frontend/src/metabase/static-viz/categorical/index.js
create mode 100644 frontend/src/metabase/static-viz/index.js
create mode 100644 frontend/src/metabase/static-viz/timeseries/bar.js
create mode 100644 frontend/src/metabase/static-viz/timeseries/index.js
create mode 100644 frontend/src/metabase/static-viz/timeseries/line.js
create mode 100644 frontend/src/metabase/static-viz/utils.js
create mode 100644 webpack.static-viz.config.js
diff --git a/bin/build-mb/src/build.clj b/bin/build-mb/src/build.clj
index d0830ed39417..34744a9a15e2 100644
--- a/bin/build-mb/src/build.clj
+++ b/bin/build-mb/src/build.clj
@@ -44,6 +44,14 @@
"NODE_ENV" "production"
"MB_EDITION" mb-edition}}
"./node_modules/.bin/webpack" "--bail"))
+ ;; related to the above TODO -- not sure why `yarn build-static-viz` fails here
+ (u/step "Build static viz"
+ (u/sh {:dir u/project-root-directory
+ :env {"PATH" (env/env :path)
+ "HOME" (env/env :user-home)
+ "NODE_ENV" "production"
+ "MB_EDITION" mb-edition}}
+ "./node_modules/.bin/webpack" "--bail" "--config" "webpack.static-viz.config.js"))
(u/announce "Frontend built successfully."))))
(def uberjar-filename (u/filename u/project-root-directory "target" "uberjar" "metabase.jar"))
diff --git a/frontend/src/metabase/static-viz/categorical/donut.js b/frontend/src/metabase/static-viz/categorical/donut.js
new file mode 100644
index 000000000000..c6901ff4bebf
--- /dev/null
+++ b/frontend/src/metabase/static-viz/categorical/donut.js
@@ -0,0 +1,64 @@
+/* eslint-disable react/prop-types */
+import React from "react";
+import { Pie } from "@visx/shape";
+import { scaleOrdinal } from "@visx/scale";
+import { Group } from "@visx/group";
+
+export default function Donut({ data, accessors }, layout) {
+ const scale = scaleOrdinal({
+ domain: data.map(accessors.dimension),
+ range: [
+ "#509ee3",
+ "#A989C5",
+ "#7172AD",
+ "#EF8C8C",
+ "#F2A86F",
+ "#88BF4D",
+ "#F9D45C",
+ "#98D9D9",
+ ],
+ });
+
+ const innerWidth = layout.width - layout.margin.left - layout.margin.right;
+ const innerHeight = layout.height - layout.margin.top - layout.margin.bottom;
+ const radius = Math.min(innerWidth, innerHeight) / 2;
+ const centerY = innerHeight / 2;
+ const centerX = innerWidth / 2;
+
+ const donutThickness = 100;
+
+ const top = centerY + layout.margin.top;
+ const left = centerX + layout.margin.left;
+
+ const pieSortValues = (a, b) => b - a;
+
+ return (
+
+ );
+}
diff --git a/frontend/src/metabase/static-viz/categorical/index.js b/frontend/src/metabase/static-viz/categorical/index.js
new file mode 100644
index 000000000000..14562178cf64
--- /dev/null
+++ b/frontend/src/metabase/static-viz/categorical/index.js
@@ -0,0 +1,3 @@
+import Donut from "./donut.js";
+
+export { Donut };
diff --git a/frontend/src/metabase/static-viz/index.js b/frontend/src/metabase/static-viz/index.js
new file mode 100644
index 000000000000..a5166be810d7
--- /dev/null
+++ b/frontend/src/metabase/static-viz/index.js
@@ -0,0 +1,48 @@
+import ReactDOMServer from "react-dom/server";
+
+import { TimeseriesBar, TimeseriesLine } from "metabase/static-viz/timeseries/";
+import { Donut } from "metabase/static-viz/categorical/";
+
+const DEFAULTS = {
+ width: 540,
+ height: 300,
+ margin: {
+ top: 20,
+ right: 20,
+ bottom: 20,
+ left: 20,
+ },
+ colors: {
+ axis: {
+ stroke: "#b8bbc3",
+ label: {
+ fill: "#949aab",
+ },
+ },
+ },
+};
+
+const TIMESERIES_BAR = "timeseries/bar";
+const TIMESERIES_LINE = "timeseries/line";
+const CATEGORICAL_DONUT = "categorical/donut";
+
+export function RenderChart(type, logic, layout = DEFAULTS) {
+ // TODO - rename as innerWidth / innerHeight
+ const xMax = layout.width - layout.margin.left - layout.margin.right;
+ const yMax = layout.height - layout.margin.top - layout.margin.bottom;
+
+ let chart;
+ switch (type) {
+ case TIMESERIES_BAR:
+ chart = TimeseriesBar(logic, { ...layout, xMax, yMax });
+ break;
+ case TIMESERIES_LINE:
+ chart = TimeseriesLine(logic, { ...layout, xMax, yMax });
+ break;
+ case CATEGORICAL_DONUT:
+ chart = Donut(logic, { ...layout, height: 540, xMax, yMax });
+ break;
+ }
+
+ return ReactDOMServer.renderToStaticMarkup(chart);
+}
diff --git a/frontend/src/metabase/static-viz/timeseries/bar.js b/frontend/src/metabase/static-viz/timeseries/bar.js
new file mode 100644
index 000000000000..da163f784a3f
--- /dev/null
+++ b/frontend/src/metabase/static-viz/timeseries/bar.js
@@ -0,0 +1,82 @@
+/* eslint-disable react/prop-types */
+import React from "react";
+import { Bar } from "@visx/shape";
+import { AxisLeft, AxisBottom } from "@visx/axis";
+import { scaleBand, scaleLinear, scaleOrdinal } from "@visx/scale";
+import { bottomAxisTickStyles, leftAxisTickStyles } from "../utils.js";
+import { GridRows } from "@visx/grid";
+
+export default function TimeseriesBar(
+ { data, yScaleType = scaleLinear, accessors },
+ layout,
+) {
+ let multiScale, categories;
+ const xAxisScale = scaleBand({
+ domain: data.map(accessors.x),
+ range: [40, layout.xMax],
+ round: true,
+ padding: 0.2,
+ });
+
+ const yAxisScale = yScaleType({
+ domain: [0, Math.max(...data.map(accessors.y))],
+ range: [layout.yMax, 0],
+ nice: true,
+ });
+
+ if (accessors.multi) {
+ categories = data.map(accessors.multi);
+ // eslint-disable-next-line no-unused-vars
+ multiScale = scaleOrdinal({
+ domain: categories,
+ range: ["blue", "yellow", "green", "red"],
+ });
+ }
+
+ return (
+
+ );
+}
diff --git a/frontend/src/metabase/static-viz/timeseries/index.js b/frontend/src/metabase/static-viz/timeseries/index.js
new file mode 100644
index 000000000000..4d44fa9864a5
--- /dev/null
+++ b/frontend/src/metabase/static-viz/timeseries/index.js
@@ -0,0 +1,4 @@
+import TimeseriesBar from "./bar.js";
+import TimeseriesLine from "./line.js";
+
+export { TimeseriesBar, TimeseriesLine };
diff --git a/frontend/src/metabase/static-viz/timeseries/line.js b/frontend/src/metabase/static-viz/timeseries/line.js
new file mode 100644
index 000000000000..e329639ae62a
--- /dev/null
+++ b/frontend/src/metabase/static-viz/timeseries/line.js
@@ -0,0 +1,88 @@
+/* eslint-disable react/prop-types */
+import React from "react";
+import { LinePath } from "@visx/shape";
+import { AxisLeft, AxisBottom } from "@visx/axis";
+import { scaleLinear, scaleOrdinal, scaleTime } from "@visx/scale";
+import { bottomAxisTickStyles, leftAxisTickStyles } from "../utils";
+import { GridRows } from "@visx/grid";
+
+export default function TimeseriesLine(
+ { data, yScaleType = scaleLinear, accessors },
+ layout,
+) {
+ let multiScale, categories;
+
+ const xAxisScale = scaleTime({
+ domain: [
+ Math.min(...data.map(accessors.x)),
+ Math.max(...data.map(accessors.x)),
+ ],
+ range: [40, layout.xMax],
+ });
+ // Y scale
+ const yAxisScale = yScaleType({
+ domain: [0, Math.max(...data.map(accessors.y))],
+ range: [layout.yMax, 0],
+ nice: true,
+ });
+
+ if (accessors.multi) {
+ multiScale = scaleOrdinal({
+ domain: data.map(accessors.multi),
+ range: ["#509ee3", "#EF8C8C", "#88BF4D", "#98D9D9", "#7173AD"],
+ });
+ categories = data.map(accessors.multi);
+ }
+
+ return (
+
+ );
+}
diff --git a/frontend/src/metabase/static-viz/utils.js b/frontend/src/metabase/static-viz/utils.js
new file mode 100644
index 000000000000..d9a7b3aba9ce
--- /dev/null
+++ b/frontend/src/metabase/static-viz/utils.js
@@ -0,0 +1,17 @@
+export function leftAxisTickStyles(layout) {
+ return {
+ fontFamily: "Lato, sans-serif",
+ fill: layout.colors.axis.label.fill,
+ fontSize: 11,
+ textAnchor: "end",
+ };
+}
+
+export function bottomAxisTickStyles(layout) {
+ return {
+ fontFamily: "Lato, sans-serif",
+ fill: layout.colors.axis.label.fill,
+ fontSize: 11,
+ textAnchor: "middle",
+ };
+}
diff --git a/package.json b/package.json
index 09c29bf9404f..03f7a2d49795 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,11 @@
"yarn": ">=1.12.3"
},
"dependencies": {
+ "@visx/axis": "1.8.0",
+ "@visx/grid": "1.16.0",
+ "@visx/group": "1.7.0",
+ "@visx/scale": "1.7.0",
+ "@visx/shape": "1.8.0",
"ace-builds": "^1.4.7",
"arg": "^5.0.0",
"chevrotain": "^6.5.0",
@@ -189,6 +194,7 @@
"build-hot": "yarn concurrently -n 'cljs,js' 'yarn build-hot:cljs' 'yarn build-hot:js'",
"build-stats": "yarn && webpack --json > stats.json",
"build-shared": "yarn && webpack --config webpack.shared.config.js",
+ "build-static-viz": "yarn && webpack --config webpack.static-viz.config.js",
"start": "yarn build && lein ring server",
"precommit": "lint-staged",
"preinstall": "echo $npm_execpath | grep -q yarn || echo '\\033[0;33mSorry, npm is not supported. Please use Yarn (https://yarnpkg.com/).\\033[0m'",
diff --git a/webpack.static-viz.config.js b/webpack.static-viz.config.js
new file mode 100644
index 000000000000..4cd05103511a
--- /dev/null
+++ b/webpack.static-viz.config.js
@@ -0,0 +1,56 @@
+const SRC_PATH = __dirname + "/frontend/src/metabase";
+const BUILD_PATH = __dirname + "/resources/frontend_client";
+
+const BABEL_CONFIG = {
+ cacheDirectory: process.env.BABEL_DISABLE_CACHE ? null : ".babel_cache",
+};
+
+module.exports = {
+ mode: "production",
+ context: SRC_PATH,
+
+ entry: {
+ "lib-static-viz": {
+ import: "./static-viz/index.js",
+ library: {
+ name: "StaticViz",
+ type: "var",
+ },
+ },
+ },
+
+ output: {
+ path: BUILD_PATH + "/app/dist",
+ filename: "[name].bundle.js",
+ },
+
+ module: {
+ rules: [
+ {
+ test: /\.(js|jsx)$/,
+ exclude: /node_modules/,
+ use: [{ loader: "babel-loader", options: BABEL_CONFIG }],
+ },
+ {
+ test: /\.(js|jsx)$/,
+ exclude: /node_modules/,
+ use: [
+ {
+ loader: "eslint-loader",
+ options: {
+ rulePaths: [__dirname + "/frontend/lint/eslint-rules"],
+ },
+ },
+ ],
+ },
+ ],
+ },
+ resolve: {
+ extensions: [".webpack.js", ".web.js", ".js", ".jsx"],
+ alias: {
+ metabase: SRC_PATH,
+ },
+ },
+
+}
+
diff --git a/yarn.lock b/yarn.lock
index b442edf93717..67352f6610b4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1148,6 +1148,54 @@
resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.0.tgz#14264692a9d6e2fa4db3df5e56e94b5e25647ac0"
integrity sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A==
+"@types/classnames@^2.2.9":
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.3.1.tgz#3c2467aa0f1a93f1f021e3b9bcf938bd5dfdc0dd"
+ integrity sha512-zeOWb0JGBoVmlQoznvqXbE0tEC/HONsnoUNH19Hc96NFsTAwTXbTqb8FMYkru1F/iqp7a18Ws3nWJvtA1sHD1A==
+ dependencies:
+ classnames "*"
+
+"@types/d3-color@^1":
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-1.4.2.tgz#944f281d04a0f06e134ea96adbb68303515b2784"
+ integrity sha512-fYtiVLBYy7VQX+Kx7wU/uOIkGQn8aAEY8oWMoyja3N4dLd8Yf6XgSIR/4yWvMuveNOH5VShnqCgRqqh/UNanBA==
+
+"@types/d3-interpolate@^1.3.1":
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-1.4.2.tgz#88902a205f682773a517612299a44699285eed7b"
+ integrity sha512-ylycts6llFf8yAEs1tXzx2loxxzDZHseuhPokrqKprTQSTcD3JbJI1omZP1rphsELZO3Q+of3ff0ZS7+O6yVzg==
+ dependencies:
+ "@types/d3-color" "^1"
+
+"@types/d3-path@^1", "@types/d3-path@^1.0.8":
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.9.tgz#73526b150d14cd96e701597cbf346cfd1fd4a58c"
+ integrity sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ==
+
+"@types/d3-scale@^3.2.1", "@types/d3-scale@^3.3.0":
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-3.3.2.tgz#18c94e90f4f1c6b1ee14a70f14bfca2bd1c61d06"
+ integrity sha512-gGqr7x1ost9px3FvIfUMi5XA/F/yAf4UkUDtdQhpH92XCT0Oa7zkkRzY61gPVJq+DxpHn/btouw5ohWkbBsCzQ==
+ dependencies:
+ "@types/d3-time" "^2"
+
+"@types/d3-shape@^1.3.1":
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.3.8.tgz#c3c15ec7436b4ce24e38de517586850f1fea8e89"
+ integrity sha512-gqfnMz6Fd5H6GOLYixOZP/xlrMtJms9BaS+6oWxTKHNqPGZ93BkWWupQSCYm6YHqx6h9wjRupuJb90bun6ZaYg==
+ dependencies:
+ "@types/d3-path" "^1"
+
+"@types/d3-time@^1.0.10":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-1.1.1.tgz#6cf3a4242c3bbac00440dfb8ba7884f16bedfcbf"
+ integrity sha512-ULX7LoqXTCYtM+tLYOaeAJK7IwCT+4Gxlm2MaH0ErKLi07R5lh8NHCAyWcDkCCmx1AfRcBEV6H9QE9R25uP7jw==
+
+"@types/d3-time@^2", "@types/d3-time@^2.0.0":
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-2.1.1.tgz#743fdc821c81f86537cbfece07093ac39b4bc342"
+ integrity sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg==
+
"@types/eslint-scope@^3.7.0":
version "3.7.0"
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.0.tgz#4792816e31119ebd506902a482caec4951fabd86"
@@ -1241,6 +1289,11 @@
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.162.tgz#65d78c397e0d883f44afbf1f7ba9867022411470"
integrity sha512-alvcho1kRUnnD1Gcl4J+hK0eencvzq9rmzvFPRmP5rPHx9VVsJj6bKLTATPVf9ktgv4ujzh7T+XWKp+jhuODig==
+"@types/lodash@^4.14.146", "@types/lodash@^4.14.160":
+ version "4.14.171"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.171.tgz#f01b3a5fe3499e34b622c362a46a609fdb23573b"
+ integrity sha512-7eQ2xYLLI/LsicL2nejW9Wyko3lcpN6O/z0ZLHrEQsg280zIdCv1t/0m6UtBjUHokCGBQ3gYTbHzDkZ1xOBwwg==
+
"@types/mdast@^3.0.0":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.4.tgz#8ee6b5200751b6cadb9a043ca39612693ad6cb9e"
@@ -1273,6 +1326,20 @@
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
+"@types/prop-types@*":
+ version "15.7.4"
+ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
+ integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
+
+"@types/react@*":
+ version "17.0.14"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.14.tgz#f0629761ca02945c4e8fea99b8177f4c5c61fb0f"
+ integrity sha512-0WwKHUbWuQWOce61UexYuWTGuGY/8JvtUe/dtQ6lR4sZ3UiylHotJeWpf3ArP9+DSGUoLY3wbU59VyMrJps5VQ==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/scheduler" "*"
+ csstype "^3.0.2"
+
"@types/redux@^3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@types/redux/-/redux-3.6.0.tgz#f1ebe1e5411518072e4fdfca5c76e16e74c1399a"
@@ -1280,6 +1347,11 @@
dependencies:
redux "*"
+"@types/scheduler@*":
+ version "0.16.2"
+ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
+ integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
+
"@types/sinonjs__fake-timers@^6.0.1":
version "6.0.2"
resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz#3a84cf5ec3249439015e14049bd3161419bf9eae"
@@ -1329,6 +1401,134 @@
dependencies:
"@types/yargs-parser" "*"
+"@visx/axis@1.8.0":
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/@visx/axis/-/axis-1.8.0.tgz#6be994af9188fe889dce6dd7c61889b0ffb7a2dd"
+ integrity sha512-DSjtYiP0Hi5m4f2x8bCQQsdEg0hwa0XdeOjRpExCRdZQfYHMo1pLrRAATgRpy7TU49394V522IVGKMi5BicpmQ==
+ dependencies:
+ "@types/classnames" "^2.2.9"
+ "@types/react" "*"
+ "@visx/group" "1.7.0"
+ "@visx/point" "1.7.0"
+ "@visx/scale" "1.7.0"
+ "@visx/shape" "1.8.0"
+ "@visx/text" "1.7.0"
+ classnames "^2.2.5"
+ prop-types "^15.6.0"
+
+"@visx/curve@1.7.0":
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/@visx/curve/-/curve-1.7.0.tgz#b8c8ae902de469ae43014c78ed9bfda8aed8137c"
+ integrity sha512-n0/SHM4YXjke+aEinhHFZPLMxWu3jbqtvqzfGJyibX8OmbDjavk9P+MHfGokUcw0xHy6Ch3YTuwbYuvVw5ny9A==
+ dependencies:
+ "@types/d3-shape" "^1.3.1"
+ d3-shape "^1.0.6"
+
+"@visx/grid@1.16.0":
+ version "1.16.0"
+ resolved "https://registry.yarnpkg.com/@visx/grid/-/grid-1.16.0.tgz#49eda6ee73cccc6ca559a6a847e2af97c38fc17a"
+ integrity sha512-WxAZ2K9itSbO/uz/6gcK8npE/doTrUz2CKBvvwKOUWjKiHkbmyk8SvNNf2yT6rMaSJ1Qxw63oUn3+j50QpcXKA==
+ dependencies:
+ "@types/classnames" "^2.2.9"
+ "@types/react" "*"
+ "@visx/curve" "1.7.0"
+ "@visx/group" "1.7.0"
+ "@visx/point" "1.7.0"
+ "@visx/scale" "1.14.0"
+ "@visx/shape" "1.16.0"
+ classnames "^2.2.5"
+ prop-types "^15.6.2"
+
+"@visx/group@1.7.0":
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/@visx/group/-/group-1.7.0.tgz#e0ef2efbe00ef05326215d65b3d8a2b114df4f35"
+ integrity sha512-rzSXtV0+MHUyK+rwhVSV4qaHdzGi3Me3PRFXJSIAKVfoJIZczOkudUOLy34WvSrRlVyoFvGL7k9U5g8wHyY3nw==
+ dependencies:
+ "@types/classnames" "^2.2.9"
+ "@types/react" "*"
+ classnames "^2.2.5"
+ prop-types "^15.6.2"
+
+"@visx/point@1.7.0":
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/@visx/point/-/point-1.7.0.tgz#1df3c3425eae464f498473bcdda2fcae05c8ecbe"
+ integrity sha512-oaoY/HXYHhmpkkeKI4rBPmFtjHWtxSrIhZCVm1ipPoyQp3voJ8L6JD5eUIVmmaUCdUGUGwL1lFLnJiQ2p1Vlwg==
+
+"@visx/scale@1.14.0":
+ version "1.14.0"
+ resolved "https://registry.yarnpkg.com/@visx/scale/-/scale-1.14.0.tgz#622d274ec4f5e608de29d06cd6071892bb1e7587"
+ integrity sha512-ovbtEOF/d76uGMJ5UZlxdS3t2T8I6md+aIwOXBaq0HdjaCLbe7HLlMyHJKjak/sqBxLAiCGVnechTUpSkfgSQw==
+ dependencies:
+ "@types/d3-interpolate" "^1.3.1"
+ "@types/d3-scale" "^3.3.0"
+ "@types/d3-time" "^2.0.0"
+ d3-interpolate "^1.4.0"
+ d3-scale "^3.3.0"
+ d3-time "^2.1.1"
+
+"@visx/scale@1.7.0":
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/@visx/scale/-/scale-1.7.0.tgz#c46daade4492edb9eaec36fd3c87dc776960d6af"
+ integrity sha512-JjAAaUPaFT6aCYTN7ILhZHk/ECg1CQ2zJZKGBbsW/HFor0mEfT/H8eSOFqI3f/DGA3eSvgmxHHBbJxyD6dB/sg==
+ dependencies:
+ "@types/d3-interpolate" "^1.3.1"
+ "@types/d3-scale" "^3.2.1"
+ "@types/d3-time" "^1.0.10"
+ d3-interpolate "^1.4.0"
+ d3-scale "^3.2.3"
+ d3-time "^1.1.0"
+
+"@visx/shape@1.16.0":
+ version "1.16.0"
+ resolved "https://registry.yarnpkg.com/@visx/shape/-/shape-1.16.0.tgz#e53182e98009ac5554d39ee1af647cc78a0e6669"
+ integrity sha512-+itAegbZiKegzDpBMRd/XQ13bR/YXzR7VcovQLzPE7GQqszq+efrrzNyjjnRLRBL9bZcCmp60aYSiDMOQVX0Tw==
+ dependencies:
+ "@types/classnames" "^2.2.9"
+ "@types/d3-path" "^1.0.8"
+ "@types/d3-shape" "^1.3.1"
+ "@types/lodash" "^4.14.146"
+ "@types/react" "*"
+ "@visx/curve" "1.7.0"
+ "@visx/group" "1.7.0"
+ "@visx/scale" "1.14.0"
+ classnames "^2.2.5"
+ d3-path "^1.0.5"
+ d3-shape "^1.2.0"
+ lodash "^4.17.15"
+ prop-types "^15.5.10"
+
+"@visx/shape@1.8.0":
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/@visx/shape/-/shape-1.8.0.tgz#e9e1d584011aabd142bf8501edf1da4b5f29dee3"
+ integrity sha512-EclCdmgfkqoAiGWsJ652dZzQIdPGKDuOkCa1qnwqPuFMjL19nSlLvI0n/yH45kWcPa5etz9iZ50LQ+6YMgvYSQ==
+ dependencies:
+ "@types/classnames" "^2.2.9"
+ "@types/d3-path" "^1.0.8"
+ "@types/d3-shape" "^1.3.1"
+ "@types/lodash" "^4.14.146"
+ "@types/react" "*"
+ "@visx/curve" "1.7.0"
+ "@visx/group" "1.7.0"
+ "@visx/scale" "1.7.0"
+ classnames "^2.2.5"
+ d3-path "^1.0.5"
+ d3-shape "^1.2.0"
+ lodash "^4.17.15"
+ prop-types "^15.5.10"
+
+"@visx/text@1.7.0":
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/@visx/text/-/text-1.7.0.tgz#530dfd85426fa87c3a43ae1d96e3148a14e7c864"
+ integrity sha512-zygMV2xSXfHHt3BLmWakTcQCk971n3vrY6iKyLEry3IyYdJy91ueKIU8OSMaKjrX0HrlT/QVP3B5QVt9knsRzg==
+ dependencies:
+ "@types/classnames" "^2.2.9"
+ "@types/lodash" "^4.14.160"
+ "@types/react" "*"
+ classnames "^2.2.5"
+ lodash "^4.17.20"
+ prop-types "^15.7.2"
+ reduce-css-calc "^1.3.0"
+
"@webassemblyjs/ast@1.11.0":
version "1.11.0"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.0.tgz#a5aa679efdc9e51707a4207139da57920555961f"
@@ -3602,7 +3802,7 @@ classlist-polyfill@^1.2.0:
resolved "https://registry.yarnpkg.com/classlist-polyfill/-/classlist-polyfill-1.2.0.tgz#935bc2dfd9458a876b279617514638bcaa964a2e"
integrity sha1-k1vC39lFiodrJ5YXUUY4vKqWSi4=
-classnames@2.3.1:
+classnames@*, classnames@2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
@@ -4482,6 +4682,13 @@ cypress@*, cypress@6.8.0, cypress@^6.8.0:
url "^0.11.0"
yauzl "^2.10.0"
+d3-array@2, d3-array@^2.3.0:
+ version "2.12.1"
+ resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81"
+ integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==
+ dependencies:
+ internmap "^1.0.0"
+
d3-array@^1.2.0:
version "1.2.4"
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f"
@@ -4497,18 +4704,40 @@ d3-color@1:
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a"
integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==
+"d3-color@1 - 2":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-2.0.0.tgz#8d625cab42ed9b8f601a1760a389f7ea9189d62e"
+ integrity sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==
+
d3-format@1:
version "1.4.5"
resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.5.tgz#374f2ba1320e3717eb74a9356c67daee17a7edb4"
integrity sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==
-d3-interpolate@1:
+"d3-format@1 - 2":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-2.0.0.tgz#a10bcc0f986c372b729ba447382413aabf5b0767"
+ integrity sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==
+
+d3-interpolate@1, d3-interpolate@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.4.0.tgz#526e79e2d80daa383f9e0c1c1c7dcc0f0583e987"
integrity sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==
dependencies:
d3-color "1"
+"d3-interpolate@1.2.0 - 2":
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-2.0.1.tgz#98be499cfb8a3b94d4ff616900501a64abc91163"
+ integrity sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==
+ dependencies:
+ d3-color "1 - 2"
+
+d3-path@1, d3-path@^1.0.5:
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf"
+ integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==
+
d3-scale@^2.1.0:
version "2.2.2"
resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.2.2.tgz#4e880e0b2745acaaddd3ede26a9e908a9e17b81f"
@@ -4521,6 +4750,24 @@ d3-scale@^2.1.0:
d3-time "1"
d3-time-format "2"
+d3-scale@^3.2.3, d3-scale@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-3.3.0.tgz#28c600b29f47e5b9cd2df9749c206727966203f3"
+ integrity sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==
+ dependencies:
+ d3-array "^2.3.0"
+ d3-format "1 - 2"
+ d3-interpolate "1.2.0 - 2"
+ d3-time "^2.1.1"
+ d3-time-format "2 - 3"
+
+d3-shape@^1.0.6, d3-shape@^1.2.0:
+ version "1.3.7"
+ resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7"
+ integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==
+ dependencies:
+ d3-path "1"
+
d3-time-format@2:
version "2.3.0"
resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.3.0.tgz#107bdc028667788a8924ba040faf1fbccd5a7850"
@@ -4528,11 +4775,25 @@ d3-time-format@2:
dependencies:
d3-time "1"
-d3-time@1:
+"d3-time-format@2 - 3":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-3.0.0.tgz#df8056c83659e01f20ac5da5fdeae7c08d5f1bb6"
+ integrity sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==
+ dependencies:
+ d3-time "1 - 2"
+
+d3-time@1, d3-time@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1"
integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==
+"d3-time@1 - 2", d3-time@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-2.1.1.tgz#e9d8a8a88691f4548e68ca085e5ff956724a6682"
+ integrity sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==
+ dependencies:
+ d3-array "2"
+
d3@^3, d3@^3.5.17:
version "3.5.17"
resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8"
@@ -7058,6 +7319,11 @@ internal-slot@^1.0.3:
has "^1.0.3"
side-channel "^1.0.4"
+internmap@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/internmap/-/internmap-1.0.1.tgz#0017cc8a3b99605f0302f2b198d272e015e5df95"
+ integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==
+
interpret@^1.0.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
@@ -8697,7 +8963,7 @@ lodash@^4.0.0, lodash@^4.0.1, lodash@^4.11.1, lodash@^4.13.1, lodash@^4.17.11, l
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
-lodash@^4.17.20:
+lodash@^4.17.15, lodash@^4.17.20:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -11583,7 +11849,7 @@ redent@^3.0.0:
indent-string "^4.0.0"
strip-indent "^3.0.0"
-reduce-css-calc@^1.2.6, reduce-css-calc@^1.2.7:
+reduce-css-calc@^1.2.6, reduce-css-calc@^1.2.7, reduce-css-calc@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"
integrity sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=
From cadd71015baf2ebfcf21a37b9945c74d8a52f712 Mon Sep 17 00:00:00 2001
From: Maz Ameli
Date: Thu, 22 Jul 2021 07:58:53 -0700
Subject: [PATCH 081/664] fix Radio component with new syntax (#17103)
---
.../metabase-enterprise/audit_app/components/AuditContent.jsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/enterprise/frontend/src/metabase-enterprise/audit_app/components/AuditContent.jsx b/enterprise/frontend/src/metabase-enterprise/audit_app/components/AuditContent.jsx
index 776fad5a170c..d7beac5ec3a1 100644
--- a/enterprise/frontend/src/metabase-enterprise/audit_app/components/AuditContent.jsx
+++ b/enterprise/frontend/src/metabase-enterprise/audit_app/components/AuditContent.jsx
@@ -17,9 +17,9 @@ export default class AuditContent extends React.Component {
{tabs && (
tab.component)} // hide tabs that aren't implemented
+ options={tabs.filter(tab => tab.component)} // hide tabs that are not implemented
optionValueFn={tab => `${pagePath}/${tab.path}`}
optionNameFn={tab => tab.title}
optionKeyFn={tab => tab.path}
From 0fdd82a2f1f0ba87d3b18c1c9fb161f6247d0d64 Mon Sep 17 00:00:00 2001
From: Kyle Doherty <5248953+kdoh@users.noreply.github.com>
Date: Thu, 22 Jul 2021 14:57:30 -0400
Subject: [PATCH 082/664] add basic static-viz internal route and examples
(#17172)
---
.../metabase/internal/pages/StaticVizPage.jsx | 71 +++++++++++++++++++
frontend/src/metabase/internal/routes.js | 2 +
2 files changed, 73 insertions(+)
create mode 100644 frontend/src/metabase/internal/pages/StaticVizPage.jsx
diff --git a/frontend/src/metabase/internal/pages/StaticVizPage.jsx b/frontend/src/metabase/internal/pages/StaticVizPage.jsx
new file mode 100644
index 000000000000..78b89ef721e6
--- /dev/null
+++ b/frontend/src/metabase/internal/pages/StaticVizPage.jsx
@@ -0,0 +1,71 @@
+import React from "react";
+import { Box } from "grid-styled";
+
+import Heading from "metabase/components/type/Heading";
+import Subhead from "metabase/components/type/Subhead";
+import Text from "metabase/components/type/Text";
+
+import { RenderChart } from "../../static-viz/";
+
+export default function StaticVizPage() {
+ return (
+
+
+ Static Visualisations
+
+ These visualizations are used in dashboard subscriptions. They have no
+ interactivity and get generated by the backend to be sent to Slack or
+ in emails. You can use this playground to work on the source code in
+ /static-viz/ and see the effects. You might need to hard refresh to
+ see updates.
+
+
+ Bar chart with timeseries data
+ new Date(row[0]).valueOf(),
+ y: row => row[1],
+ },
+ }),
+ }}
+ >
+
+
+ Line chart with timeseries data
+ new Date(row[0]).valueOf(),
+ y: row => row[1],
+ },
+ }),
+ }}
+ >
+
+
+ Donut chart showing categorical data
+ row[0],
+ metric: row => row[1],
+ },
+ }),
+ }}
+ >
+
+
+
+ );
+}
diff --git a/frontend/src/metabase/internal/routes.js b/frontend/src/metabase/internal/routes.js
index a2d4256f4a98..01bce71292c8 100644
--- a/frontend/src/metabase/internal/routes.js
+++ b/frontend/src/metabase/internal/routes.js
@@ -28,6 +28,7 @@ import IconsPage from "metabase/internal/pages/IconsPage";
import ColorsPage from "metabase/internal/pages/ColorsPage";
import ComponentsPage from "metabase/internal/pages/ComponentsPage";
import ModalsPage from "metabase/internal/pages/ModalsPage";
+import StaticVizPage from "metabase/internal/pages/StaticVizPage";
import { InternalLayout } from "metabase/internal/components/Layout";
@@ -42,6 +43,7 @@ export default (
+
{/* Legacy App pages - not really style guide related, but keep for now */}
{Object.entries(APPS).map(
([name, Component]) =>
From fc47146132d5166a6a0bdf4ab6f63c1bbb5c8ca6 Mon Sep 17 00:00:00 2001
From: Alexander Lesnenko
Date: Thu, 22 Jul 2021 23:43:25 +0300
Subject: [PATCH 083/664] change iframe-resizer height calculation method to
max (#17158)
---
frontend/src/metabase/lib/dom.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frontend/src/metabase/lib/dom.js b/frontend/src/metabase/lib/dom.js
index edc193c1b86f..cd140a423623 100644
--- a/frontend/src/metabase/lib/dom.js
+++ b/frontend/src/metabase/lib/dom.js
@@ -396,7 +396,7 @@ export function initializeIframeResizer(readyCallback = () => {}) {
} else {
window.iFrameResizer = {
autoResize: true,
- heightCalculationMethod: "bodyScroll",
+ heightCalculationMethod: "max",
readyCallback: readyCallback,
};
From eb59ab27a7de40227f0ee13185ba530dc23ddc81 Mon Sep 17 00:00:00 2001
From: Jeff Evans
Date: Thu, 22 Jul 2021 18:04:05 -0500
Subject: [PATCH 084/664] Fix serialization: Visualization column settings lost
(#17127)
Update visualization_settings.cljc to handle the table.columns submap
Update dump and load to handle :visualization_settings on a Card (basically identically to how they are handled for a DashboardCard)
Updating test to have a card include :visualization_settings and ensure they fields are properly handled
Updating visualization_settings test case to incorporate table.columns as well as :show_mini_bar (under ::column_settings)
---
.../serialization/load.clj | 42 +++++++++++++++--
.../serialization/serialize.clj | 3 +-
.../serialization/load_test.clj | 35 ++++++++++++--
.../serialization/test_util.clj | 11 ++++-
.../shared/models/visualization_settings.cljc | 47 ++++++++++++++++---
.../models/visualization_settings_test.cljc | 24 ++++++++--
6 files changed, 141 insertions(+), 21 deletions(-)
diff --git a/enterprise/backend/src/metabase_enterprise/serialization/load.clj b/enterprise/backend/src/metabase_enterprise/serialization/load.clj
index 1fa4d005cd4e..a09450417799 100644
--- a/enterprise/backend/src/metabase_enterprise/serialization/load.clj
+++ b/enterprise/backend/src/metabase_enterprise/serialization/load.clj
@@ -135,8 +135,16 @@
necessary when dealing with the full MBQL query tree (which can have arbitrary nesting of maps and
vectors)."
[m]
- (reduce (fn [acc ks]
- (pull-unresolved-names-up acc (drop-last ks))) m (paths-to-key-in m ::unresolved-names)))
+ (let [paths (paths-to-key-in m ::unresolved-names)]
+ (if-not (empty? paths)
+ (reduce (fn [acc ks]
+ (let [ks* (drop-last ks)]
+ (if-not (empty? ks*)
+ (pull-unresolved-names-up acc ks*)
+ acc)))
+ m
+ paths)
+ m)))
(defn- mbql-fully-qualified-names->ids*
[entity]
@@ -376,18 +384,46 @@
(pull-unresolved-names-up vs-norm [::mb.viz/click-behavior] resolved-cb))
vs-norm))
-(defn- resolve-column-settings [vs-norm]
+(defn- resolve-column-settings
+ "Resolve the entries in a :column_settings map (which is under a :visualization_settings map). These map entries
+ may contain fully qualified field names, or even other cards. In case of an unresolved name (i.e. a card that hasn't
+ yet been loaded), we will track it under ::unresolved-names and revisit on the next pass."
+ [vs-norm]
(if-let [col-settings (::mb.viz/column-settings vs-norm)]
(let [resolved-cs (reduce-kv accumulate-converted-column-settings {} col-settings)]
(pull-unresolved-names-up vs-norm [::mb.viz/column-settings] resolved-cs))
vs-norm))
+(defn- resolve-table-columns
+ "Resolve the :table.columns key from a :visualization_settings map, which may contain fully qualified field names.
+ Such fully qualified names will be converted to the numeric field ID before being filled into the loaded card. Only
+ other field names (not cards, or other collection based entity types) should be referenced here, so there is no need
+ to detect or track ::unresolved-names."
+ [vs-norm]
+ (if (::mb.viz/table-columns vs-norm)
+ (letfn [(resolve-table-column-field-ref [[f-type f-str f-md]]
+ (if (names/fully-qualified-field-name? f-str)
+ [f-type ((comp :field fully-qualified-name->context) f-str) f-md]
+ [f-type f-str f-md]))
+ (resolve-field-id [{:keys [::mb.viz/table-column-field-ref] :as tbl-col}]
+ (update tbl-col ::mb.viz/table-column-field-ref resolve-table-column-field-ref))]
+ (update vs-norm ::mb.viz/table-columns (fn [tbl-cols]
+ (mapv resolve-field-id tbl-cols))))
+ vs-norm))
+
(defn- resolve-visualization-settings
+ "Resolve all references from a :visualization_settings map, the various submaps of which may contain:
+ - fully qualified field names
+ - fully qualified card or dashboard names
+
+ Any unresolved entities from this resolution process will be tracked via ::unresolved-named so that the card or
+ dashboard card holding these visualization settings can be revisited in a future pass."
[entity]
(if-let [viz-settings (:visualization_settings entity)]
(let [resolved-vs (-> (mb.viz/db->norm viz-settings)
resolve-top-level-click-behavior
resolve-column-settings
+ resolve-table-columns
mb.viz/norm->db)]
(pull-unresolved-names-up entity [:visualization_settings] resolved-vs))
entity))
diff --git a/enterprise/backend/src/metabase_enterprise/serialization/serialize.clj b/enterprise/backend/src/metabase_enterprise/serialization/serialize.clj
index d02916dec508..e500e2dbec14 100644
--- a/enterprise/backend/src/metabase_enterprise/serialization/serialize.clj
+++ b/enterprise/backend/src/metabase_enterprise/serialization/serialize.clj
@@ -208,7 +208,8 @@
[card]
(-> card
(m/update-existing :table_id (partial fully-qualified-name Table))
- (update :database_id (partial fully-qualified-name Database))))
+ (update :database_id (partial fully-qualified-name Database))
+ (m/update-existing :visualization_settings convert-viz-settings)))
(defmethod serialize-one (type Pulse)
[pulse]
diff --git a/enterprise/backend/test/metabase_enterprise/serialization/load_test.clj b/enterprise/backend/test/metabase_enterprise/serialization/load_test.clj
index c07196d26529..fa14d3c38507 100644
--- a/enterprise/backend/test/metabase_enterprise/serialization/load_test.clj
+++ b/enterprise/backend/test/metabase_enterprise/serialization/load_test.clj
@@ -19,7 +19,8 @@
[metabase.shared.util.log :as log]
[metabase.test :as mt]
[metabase.test.fixtures :as fixtures]
- [metabase.util.i18n :refer [deferred-trs trs]])
+ [metabase.util.i18n :refer [deferred-trs trs]]
+ [clojure.string :as str])
(:import org.apache.commons.io.FileUtils))
(use-fixtures :once
@@ -110,9 +111,33 @@
(type entity)))
(defmethod assert-loaded-entity (type Card)
- [card {:keys [query-results collections]}]
- (query-res-match query-results card)
- (collection-names-match collections card))
+ [{card-name :name :as card} {:keys [query-results collections]}]
+ (testing (format "Card: %s" card-name)
+ (query-res-match query-results card)
+ (collection-names-match collections card)
+ (when (= "My Nested Card" card-name)
+ (testing "Visualization settings for a Card were persisted correctly"
+ (let [vs (:visualization_settings card)
+ col (-> (:column_settings vs)
+ first)
+ [col-key col-val] col
+ col-ref (mb.viz/parse-db-column-ref col-key)
+ {:keys [::mb.viz/field-id]} col-ref
+ [{col-name :name col-field-ref :fieldRef col-enabled :enabled :as tbl-col} & _] (:table.columns vs)
+ [_ col-field-id _] col-field-ref]
+ (is (some? (:table.columns vs)))
+ (is (some? (:column_settings vs)))
+ (is (integer? field-id))
+ (is (= "latitude" (-> (db/select-one-field :name Field :id field-id)
+ str/lower-case)))
+ (is (= {:show_mini_bar true
+ :column_title "Parallel"} col-val))
+ (is (= "Venue Category" col-name))
+ (is (true? col-enabled))
+ (is (integer? col-field-id) "fieldRef within table.columns was properly serialized and loaded")
+ (is (= "category_id" (-> (db/select-one-field :name Field :id col-field-id)
+ str/lower-case))))))
+ card))
(defn- collection-parent-name [collection]
(let [[_ parent-id] (re-matches #".*/(\d+)/$" (:location collection))]
@@ -235,7 +260,7 @@
[entity _]
entity)
-(deftest dump-load-entities-test
+(deftest dump-load-entities-testw
(try
;; in case it already exists
(u/ignore-exceptions
diff --git a/enterprise/backend/test/metabase_enterprise/serialization/test_util.clj b/enterprise/backend/test/metabase_enterprise/serialization/test_util.clj
index 901b44dea159..f1676b4ff37f 100644
--- a/enterprise/backend/test/metabase_enterprise/serialization/test_util.clj
+++ b/enterprise/backend/test/metabase_enterprise/serialization/test_util.clj
@@ -146,7 +146,16 @@
:collection_id ~'collection-id
:dataset_query {:type :query
:database ~'db-id
- :query {:source-table (str "card__" ~'card-id)}}}]
+ :query {:source-table (str "card__" ~'card-id)}}
+ :visualization_settings
+ {:table.columns [{:name "Venue Category"
+ :fieldRef [:field ~'category-field-id nil]
+ :enabled true}]
+ :column_settings {(keyword (format
+ "[\"ref\",[\"field\",%d,null]]"
+ ~'latitude-field-id))
+ {:show_mini_bar true
+ :column_title "Parallel"}}}}]
Card [{~'card-id-nested-query :id}
{:table_id ~'table-id
:name "My Nested Query Card"
diff --git a/shared/src/metabase/shared/models/visualization_settings.cljc b/shared/src/metabase/shared/models/visualization_settings.cljc
index 07341bc22317..9f00df1aa375 100644
--- a/shared/src/metabase/shared/models/visualization_settings.cljc
+++ b/shared/src/metabase/shared/models/visualization_settings.cljc
@@ -53,9 +53,11 @@
(s/def ::click-behavior (s/keys))
(s/def ::visualization-settings (s/keys :opt [::column-settings ::click-behavior]))
-(s/def ::db-column-ref-vec (s/or :field (s/tuple (partial = "ref") (s/tuple (partial = "field")
- (s/or :field-id int? :field-str string?)
- (s/or :field-metadata map? :nil nil?)))
+(s/def ::field-id-vec (s/tuple (partial = "ref") (s/tuple (partial = "field")
+ (s/or :field-id int? :field-str string?)
+ (s/or :field-metadata map? :nil nil?))))
+
+(s/def ::db-column-ref-vec (s/or :field ::field-id-vec
:column-name (s/tuple (partial = "name") string?)))
(s/def ::click-behavior-type keyword? #_(s/or :cross-filter ::cross-filter
@@ -285,7 +287,6 @@
::link-target-id entity-id}
(some? parameter-mapping) (assoc ::parameter-mapping parameter-mapping)))
-
(s/fdef entity-click-action
:args (s/cat :entity-type ::entity-type :entity-id int? :parameter-mapping ::parameter-mapping)
:ret ::click-behavior)
@@ -322,7 +323,7 @@
:from-field-id int?
:to-entity-type ::entity-type
:to-entity-id int?
- :parameter-mapping (s/? ::parameter-mapping) )
+ :parameter-mapping (s/? ::parameter-mapping))
:ret ::click-behavior)
(defn fk-parameter-mapping
@@ -382,7 +383,8 @@
:prefix ::prefix
:suffix ::suffix
:view_as ::view-as
- :link_text ::link-text})
+ :link_text ::link-text
+ :show_mini_bar ::show-mini-barchart})
(def ^:private norm->db-column-settings-keys
(set/map-invert db->norm-column-settings-keys))
@@ -404,6 +406,17 @@
(def ^:private norm->db-param-ref-keys
(set/map-invert db->norm-param-ref-keys))
+(def ^:private db->norm-table-columns-keys
+ {:name ::table-column-name
+ ; for now, do not translate the value of this key (the field vector)
+ :fieldRef ::table-column-field-ref
+ :enabled ::table-column-enabled})
+
+(def ^:private norm->db-table-columns-keys
+ (set/map-invert db->norm-table-columns-keys))
+
+(s/def ::table-column-field-ref ::field-id-vec)
+
(defn- db->norm-param-ref [parsed-id param-ref]
(cond-> (set/rename-keys param-ref db->norm-param-ref-keys)
(= "dimension" (:type param-ref)) (assoc ::param-ref-id parsed-id)))
@@ -493,6 +506,13 @@
(dissoc :parameterMapping)
(set/rename-keys db->norm-click-behavior-keys)))
+(defn- db->norm-table-columns [v]
+ (-> v
+ (assoc ::table-columns (mapv (fn [tbl-col]
+ (set/rename-keys tbl-col db->norm-table-columns-keys))
+ (:table.columns v)))
+ (dissoc :table.columns)))
+
(defn- db->norm-column-settings-entry
"Converts a :column_settings DB form to qualified form. Does the opposite of
`norm->db-column-settings-entry`."
@@ -521,6 +541,9 @@
(:click_behavior vs)
(assoc ::click-behavior (db->norm-click-behavior (:click_behavior vs)))
+ (:table.columns vs)
+ db->norm-table-columns
+
:always
(dissoc :column_settings :click_behavior)))
@@ -574,6 +597,15 @@
(m/map-kv (fn [k v]
[(norm->db-column-ref k) (reduce-kv norm->db-column-settings-entry {} v)]))))
+(defn- norm->db-table-columns [v]
+ (cond-> v
+ (some? (::table-columns v))
+ (assoc :table.columns (mapv (fn [tbl-col]
+ (set/rename-keys tbl-col norm->db-table-columns-keys))
+ (::table-columns v)))
+ :always
+ (dissoc ::table-columns)))
+
(defn norm->db
"Converts the normalized form of visualization settings (i.e. a map having
`::column-settings` into the equivalent DB form (i.e. a map having `:column_settings`).
@@ -587,4 +619,5 @@
(dissoc ::column-settings))
(::click-behavior settings) (-> ; from cond->
(assoc :click_behavior (norm->db-click-behavior-value (::click-behavior settings)))
- (dissoc ::click-behavior))))
+ (dissoc ::click-behavior))
+ (::table-columns settings) norm->db-table-columns))
diff --git a/shared/test/metabase/shared/models/visualization_settings_test.cljc b/shared/test/metabase/shared/models/visualization_settings_test.cljc
index f736c8736268..4a761611802e 100644
--- a/shared/test/metabase/shared/models/visualization_settings_test.cljc
+++ b/shared/test/metabase/shared/models/visualization_settings_test.cljc
@@ -61,17 +61,33 @@
:parameterMapping {}
:targetId target-id}
db-click-bhv-map {:click_behavior db-click-behavior}
+ col-nm-map {:show_mini_bar true
+ :column_title "Name Column"}
db-col-settings {(fmt "[\"ref\",[\"field\",%d,{\"base-type\":\"type/Integer\"}]]" f-id) db-click-bhv-map
- (fmt "[\"name\",\"%s\"]" col-name) db-click-bhv-map}
- db-viz-settings {:column_settings db-col-settings}
+ (fmt "[\"name\",\"%s\"]" col-name) col-nm-map}
+ db-viz-settings {:column_settings db-col-settings
+ :table.columns [{:name "ID"
+ :fieldRef [:field f-id nil]
+ :enabled true}
+ {:name "Name"
+ :fieldRef [:expression col-name]
+ :enabled true}]}
norm-click-behavior {::mb.viz/click-behavior-type ::mb.viz/link
::mb.viz/link-type ::mb.viz/card
::mb.viz/parameter-mapping {}
::mb.viz/link-target-id target-id}
+ norm-col-nm {::mb.viz/column-title "Name Column"
+ ::mb.viz/show-mini-barchart true}
norm-click-bhvr-map {::mb.viz/click-behavior norm-click-behavior}
norm-col-settings {(mb.viz/field-id->column-ref f-id {"base-type" "type/Integer"}) norm-click-bhvr-map
- (mb.viz/column-name->column-ref col-name) norm-click-bhvr-map}
- norm-viz-settings {::mb.viz/column-settings norm-col-settings}]
+ (mb.viz/column-name->column-ref col-name) norm-col-nm}
+ norm-viz-settings {::mb.viz/column-settings norm-col-settings
+ ::mb.viz/table-columns [{::mb.viz/table-column-name "ID"
+ ::mb.viz/table-column-field-ref [:field f-id nil]
+ ::mb.viz/table-column-enabled true}
+ {::mb.viz/table-column-name "Name"
+ ::mb.viz/table-column-field-ref [:expression col-name]
+ ::mb.viz/table-column-enabled true}]}]
(doseq [[db-form norm-form] [[db-viz-settings norm-viz-settings]]]
(let [to-norm (mb.viz/db->norm db-form)]
(t/is (= norm-form to-norm))
From 940154d9a5f9da5745d5259de1dff4eb266a9901 Mon Sep 17 00:00:00 2001
From: Jeff Evans
Date: Thu, 22 Jul 2021 18:30:10 -0500
Subject: [PATCH 085/664] Remove regionid from Snowflake config (#16740)
Remove regionid from plugin YAML file
Add new driver multimethod to `normalize db-details`, and implementing to handle this particular "migration" within Snowflake
Adding hook from database model post-select to invoke the new multi
Adding test
---
.../snowflake/resources/metabase-plugin.yaml | 3 ---
.../snowflake/src/metabase/driver/snowflake.clj | 17 +++++++++++------
.../test/metabase/driver/snowflake_test.clj | 10 ++++++++++
src/metabase/driver.clj | 15 +++++++++++++++
src/metabase/models/database.clj | 9 +++++++--
5 files changed, 43 insertions(+), 11 deletions(-)
diff --git a/modules/drivers/snowflake/resources/metabase-plugin.yaml b/modules/drivers/snowflake/resources/metabase-plugin.yaml
index 39f3e80f047e..b235c041beeb 100644
--- a/modules/drivers/snowflake/resources/metabase-plugin.yaml
+++ b/modules/drivers/snowflake/resources/metabase-plugin.yaml
@@ -25,9 +25,6 @@ driver:
- name: db
required: true
display-name: Database name (case sensitive)
- - name: regionid
- display-name: Region ID
- placeholder: my_region
- name: schema
display-name: Schema
placeholder: my_schema
diff --git a/modules/drivers/snowflake/src/metabase/driver/snowflake.clj b/modules/drivers/snowflake/src/metabase/driver/snowflake.clj
index 04251dbce438..04e459569d9b 100644
--- a/modules/drivers/snowflake/src/metabase/driver/snowflake.clj
+++ b/modules/drivers/snowflake/src/metabase/driver/snowflake.clj
@@ -6,6 +6,7 @@
[clojure.tools.logging :as log]
[honeysql.core :as hsql]
[java-time :as t]
+ [medley.core :as m]
[metabase.driver :as driver]
[metabase.driver.common :as driver.common]
[metabase.driver.sql-jdbc :as sql-jdbc]
@@ -42,16 +43,13 @@
:sunday)
(defmethod sql-jdbc.conn/connection-details->spec :snowflake
- [_ {:keys [account regionid], :as opts}]
- (let [host (if regionid
- (str account "." regionid)
- account)
- upcase-not-nil (fn [s] (when s (u/upper-case-en s)))]
+ [_ {:keys [account], :as opts}]
+ (let [upcase-not-nil (fn [s] (when s (u/upper-case-en s)))]
;; it appears to be the case that their JDBC driver ignores `db` -- see my bug report at
;; https://support.snowflake.net/s/question/0D50Z00008WTOMCSA5/
(-> (merge {:classname "net.snowflake.client.jdbc.SnowflakeDriver"
:subprotocol "snowflake"
- :subname (str "//" host ".snowflakecomputing.com/")
+ :subname (str "//" account ".snowflakecomputing.com/")
:client_metadata_request_use_connection_ctx true
:ssl true
;; keep open connections open indefinitely instead of closing them. See #9674 and
@@ -306,6 +304,13 @@
(jdbc/query spec sql)
true)))
+(defmethod driver/normalize-db-details :snowflake
+ [_ database]
+ (if-not (str/blank? (-> database :details :regionid))
+ (-> (update-in database [:details :account] #(str/join "." [% (-> database :details :regionid)]))
+ (m/dissoc-in [:details :regionid]))
+ database))
+
(defmethod unprepare/unprepare-value [:snowflake OffsetDateTime]
[_ t]
(format "timestamp '%s %s %s'" (t/local-date t) (t/local-time t) (t/zone-offset t)))
diff --git a/modules/drivers/snowflake/test/metabase/driver/snowflake_test.clj b/modules/drivers/snowflake/test/metabase/driver/snowflake_test.clj
index 5817ad56270a..502e8d2c2292 100644
--- a/modules/drivers/snowflake/test/metabase/driver/snowflake_test.clj
+++ b/modules/drivers/snowflake/test/metabase/driver/snowflake_test.clj
@@ -178,3 +178,13 @@
["2014-08-02T00:00:00-07:00" "2014-08-02T02:30:00-07:00"]]
(mt/with-temporary-setting-values [report-timezone "US/Pacific"]
(run-query)))))))))
+
+(deftest normalize-test
+ (mt/test-driver :snowflake
+ (testing "details should be normalized coming out of the DB"
+ (mt/with-temp Database [db {:name "Legacy Snowflake DB"
+ :engine :snowflake,
+ :details {:account "my-instance"
+ :regionid "us-west-1"}}]
+ (is (= {:account "my-instance.us-west-1"}
+ (:details db)))))))
diff --git a/src/metabase/driver.clj b/src/metabase/driver.clj
index 0e8d5e3ad3cb..23f800d8d519 100644
--- a/src/metabase/driver.clj
+++ b/src/metabase/driver.clj
@@ -602,3 +602,18 @@
{:added "0.39.0" :arglists '([driver db-details])}
dispatch-on-uninitialized-driver
:hierarchy #'hierarchy)
+
+(defmulti normalize-db-details
+ "Normalizes db-details for the given driver. This is to handle migrations that are too difficult to perform via
+ regular Liquibase queries. This multimethod will be called from a `:post-select` handler within the database model.
+ The full `database` model object is passed as the 2nd parameter, and the multimethod implementation is expected to
+ update the value for `:details`. The default implementation is essentially `identity` (i.e returns `database`
+ unchanged). This multimethod will only be called if `:details` is actually present in the `database` map."
+ {:added "0.41.0" :arglists '([driver database])}
+ dispatch-on-initialized-driver
+ :hierarchy #'hierarchy)
+
+(defmethod normalize-db-details ::driver
+ [_ db-details]
+ ;; no normalization by default
+ db-details)
diff --git a/src/metabase/models/database.clj b/src/metabase/models/database.clj
index e57326d24985..c59420e449cc 100644
--- a/src/metabase/models/database.clj
+++ b/src/metabase/models/database.clj
@@ -49,9 +49,14 @@
(schedule-tasks! database)))
(defn- post-select [{driver :engine, :as database}]
- ;; TODO - this is only really needed for API responses. This should be a `hydrate` thing instead!
(cond-> database
- (driver/initialized? driver) (assoc :features (driver.u/features driver))))
+ (driver/initialized? driver)
+ ;; TODO - this is only really needed for API responses. This should be a `hydrate` thing instead!
+ (as-> db* ; database from outer cond->
+ (assoc db* :features (driver.u/features driver))
+ (if (:details db*)
+ (driver/normalize-db-details driver db*)
+ db*))))
(defn- pre-delete [{id :id, driver :engine, :as database}]
(unschedule-tasks! database)
From a8d1e9462760942b7318d58cbdb5518282ebbef2 Mon Sep 17 00:00:00 2001
From: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Date: Fri, 23 Jul 2021 13:52:41 +0200
Subject: [PATCH 086/664] Upgrade `xlsx` library (#17181)
---
package.json | 2 +-
yarn.lock | 14 ++++++++++----
2 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/package.json b/package.json
index 03f7a2d49795..b5f7f8b889b5 100644
--- a/package.json
+++ b/package.json
@@ -160,7 +160,7 @@
"webpack-notifier": "^1.8.0",
"webpack-postcss-tools": "^1.1.2",
"xhr-mock": "^2.4.1",
- "xlsx": "^0.16.8",
+ "xlsx": "^0.17.0",
"yaml-lint": "^1.2.4"
},
"scripts": {
diff --git a/yarn.lock b/yarn.lock
index 67352f6610b4..4f9449cf0e90 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6093,6 +6093,11 @@ fd-slicer@~1.1.0:
dependencies:
pend "~1.2.0"
+fflate@^0.3.8:
+ version "0.3.11"
+ resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.3.11.tgz#2c440d7180fdeb819e64898d8858af327b042a5d"
+ integrity sha512-Rr5QlUeGN1mbOHlaqcSYMKVpPbgLy0AWT/W0EHxA6NGI12yO1jpoui2zBBvU2G824ltM6Ut8BFgfHSBGfkmS0A==
+
figures@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
@@ -14815,10 +14820,10 @@ xhr-mock@^2.4.1:
global "^4.3.0"
url "^0.11.0"
-xlsx@^0.16.8:
- version "0.16.8"
- resolved "https://registry.yarnpkg.com/xlsx/-/xlsx-0.16.8.tgz#5546de9b0ba15169b36770d4e43b24790d3ff1b8"
- integrity sha512-qWub4YCn0xLEGHI7WWhk6IJ73MDu7sPSJQImxN6/LiI8wsHi0hUhICEDbyqBT+jgFgORZxrii0HvhNSwBNAPoQ==
+xlsx@^0.17.0:
+ version "0.17.0"
+ resolved "https://registry.yarnpkg.com/xlsx/-/xlsx-0.17.0.tgz#028176a0140967dcee1817d221678461e47481c8"
+ integrity sha512-bZ36FSACiAyjoldey1+7it50PMlDp1pcAJrZKcVZHzKd8BC/z6TQ/QAN8onuqcepifqSznR6uKnjPhaGt6ig9A==
dependencies:
adler-32 "~1.2.0"
cfb "^1.1.4"
@@ -14826,6 +14831,7 @@ xlsx@^0.16.8:
commander "~2.17.1"
crc-32 "~1.2.0"
exit-on-epipe "~1.0.1"
+ fflate "^0.3.8"
ssf "~0.11.2"
wmf "~1.0.1"
word "~0.3.0"
From 3d3c63d1c9fe86d1e7f3b9adcb52d9099885ec30 Mon Sep 17 00:00:00 2001
From: Gustavo Saiani
Date: Fri, 23 Jul 2021 09:15:14 -0300
Subject: [PATCH 087/664] Improve Collection Sidebar component (#17114)
---
.../CollectionSidebar/CollectionSidebar.jsx | 121 ++++++------------
.../CollectionSidebar.styled.js | 18 ++-
.../CollectionSidebarFooter.jsx | 30 +++++
.../CollectionSidebarFooter.styled.js | 31 +++++
.../CollectionSidebarFooter.unit.spec.js | 28 ++++
.../Collections/Collections.jsx | 65 ++++++++++
.../Collections/Collections.styled.js | 8 ++
.../Collections/Collections.unit.spec.js | 26 ++++
.../RootCollectionLink/RootCollectionLink.jsx | 36 ++++++
.../RootCollectionLink.styled.jsx | 9 ++
.../RootCollectionLink.unit.spec.js | 18 +++
11 files changed, 301 insertions(+), 89 deletions(-)
create mode 100644 frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarFooter/CollectionSidebarFooter.jsx
create mode 100644 frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarFooter/CollectionSidebarFooter.styled.js
create mode 100644 frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarFooter/CollectionSidebarFooter.unit.spec.js
create mode 100644 frontend/src/metabase/collections/containers/CollectionSidebar/Collections/Collections.jsx
create mode 100644 frontend/src/metabase/collections/containers/CollectionSidebar/Collections/Collections.styled.js
create mode 100644 frontend/src/metabase/collections/containers/CollectionSidebar/Collections/Collections.unit.spec.js
create mode 100644 frontend/src/metabase/collections/containers/CollectionSidebar/RootCollectionLink/RootCollectionLink.jsx
create mode 100644 frontend/src/metabase/collections/containers/CollectionSidebar/RootCollectionLink/RootCollectionLink.styled.jsx
create mode 100644 frontend/src/metabase/collections/containers/CollectionSidebar/RootCollectionLink/RootCollectionLink.unit.spec.js
diff --git a/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebar.jsx b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebar.jsx
index 1d319ada9632..0469fadfb890 100644
--- a/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebar.jsx
+++ b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebar.jsx
@@ -1,29 +1,24 @@
/* eslint-disable react/prop-types */
import React from "react";
import { connect } from "react-redux";
-import { Box } from "grid-styled";
import { t } from "ttag";
-import * as Urls from "metabase/lib/urls";
-
import Collection from "metabase/entities/collections";
-import CollectionDropTarget from "metabase/containers/dnd/CollectionDropTarget";
+import {
+ LoadingContainer,
+ LoadingTitle,
+ Sidebar,
+ ToggleMobileSidebarIcon,
+} from "./CollectionSidebar.styled";
-import { Sidebar, ToggleMobileSidebarIcon } from "./CollectionSidebar.styled";
-import Icon from "metabase/components/Icon";
-import Link from "metabase/components/Link";
-import LoadingSpinner from "metabase/components/LoadingSpinner";
+import RootCollectionLink from "./RootCollectionLink/RootCollectionLink";
+import Footer from "./CollectionSidebarFooter/CollectionSidebarFooter";
+import Collections from "./Collections/Collections";
-import CollectionsList from "metabase/collections/components/CollectionsList";
-import CollectionLink from "metabase/collections/components/CollectionLink";
+import LoadingSpinner from "metabase/components/LoadingSpinner";
-import { SIDEBAR_SPACER } from "metabase/collections/constants";
-import {
- nonPersonalOrArchivedCollection,
- currentUserPersonalCollections,
- getParentPath,
-} from "metabase/collections/utils";
+import { getParentPath } from "metabase/collections/utils";
const getCurrentUser = ({ currentUser }) => ({ currentUser });
@@ -47,6 +42,7 @@ class CollectionSidebar extends React.Component {
componentDidUpdate(prevProps) {
const { collectionId, collections, loading } = this.props;
const loaded = prevProps.loading && !loading;
+
if (loaded) {
const ancestors = getParentPath(collections, collectionId) || [];
this.setState({ openCollections: ancestors });
@@ -65,9 +61,6 @@ class CollectionSidebar extends React.Component {
});
};
- // TODO Should we update the API to filter archived collections?
- filterPersonalCollections = collection => !collection.archived;
-
renderContent = () => {
const {
currentUser,
@@ -79,90 +72,48 @@ class CollectionSidebar extends React.Component {
return (
+
{({ collection: root }) => (
-
-
- {({ highlighted, hovered }) => (
-
- {t`Our analytics`}
-
- )}
-
-
+
)}
-
-
-
-
-
-
-
-
-
- {currentUser.is_superuser && (
-
-
- {t`Other users' personal collections`}
-
- )}
-
-
- {t`View archive`}
-
-
+
+
+
);
};
render() {
- const { allFetched } = this.props;
+ const { allFetched, shouldDisplayMobileSidebar } = this.props;
return (
- {allFetched ? (
- this.renderContent()
- ) : (
-
-
- {t`Loading…`}
-
- )}
+ {allFetched ? this.renderContent() : }
);
}
}
+function LoadingView() {
+ return (
+
+
+ {t`Loading…`}
+
+ );
+}
+
export default connect(getCurrentUser)(CollectionSidebar);
diff --git a/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebar.styled.js b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebar.styled.js
index 9cd49d06a857..af556262a0da 100644
--- a/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebar.styled.js
+++ b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebar.styled.js
@@ -1,12 +1,24 @@
import styled from "styled-components";
import { Box } from "grid-styled";
+import { space } from "metabase/styled-components/theme";
import { color } from "metabase/lib/colors";
import Icon from "metabase/components/Icon";
import { breakpointMinSmall } from "metabase/styled-components/theme/media-queries";
import { SIDEBAR_WIDTH } from "metabase/collections/constants";
+export const LoadingContainer = styled.div`
+ color: ${color("brand")};
+ text-align: center;
+`;
+
+export const LoadingTitle = styled.h2`
+ color: ${color("text-light")};
+ font-weight: 400;
+ margin-top: ${space(1)};
+`;
+
export const Sidebar = styled(Box.withComponent("aside"))`
bottom: 0;
display: flex;
@@ -14,7 +26,7 @@ export const Sidebar = styled(Box.withComponent("aside"))`
left: 0;
overflow-x: hidden;
overflow-y: auto;
- padding-top: 32px;
+ padding-top: ${space(3)};
position: fixed;
top: 65px;
width: ${props => (props.shouldDisplayMobileSidebar ? "100vw" : 0)};
@@ -25,13 +37,11 @@ export const Sidebar = styled(Box.withComponent("aside"))`
`;
export const ToggleMobileSidebarIcon = styled(Icon).attrs({
- ml: 3,
- mr: 2,
- mt: "4px",
name: "close",
size: 20,
})`
color: ${color("brand")};
+ margin: ${space(0)} ${space(2)} 0 ${space(3)}};
${breakpointMinSmall} {
cursor: pointer;
diff --git a/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarFooter/CollectionSidebarFooter.jsx b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarFooter/CollectionSidebarFooter.jsx
new file mode 100644
index 000000000000..3d718010e32a
--- /dev/null
+++ b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarFooter/CollectionSidebarFooter.jsx
@@ -0,0 +1,30 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { t } from "ttag";
+
+import * as Urls from "metabase/lib/urls";
+import { Container, Icon, Link } from "./CollectionSidebarFooter.styled";
+
+const propTypes = {
+ isAdmin: PropTypes.bool.isRequired,
+};
+
+export default function CollectionSidebarFooter({ isAdmin }) {
+ return (
+
+ {isAdmin && (
+
+
+ {t`Other users' personal collections`}
+
+ )}
+
+
+
+ {t`View archive`}
+
+
+ );
+}
+
+CollectionSidebarFooter.propTypes = propTypes;
diff --git a/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarFooter/CollectionSidebarFooter.styled.js b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarFooter/CollectionSidebarFooter.styled.js
new file mode 100644
index 000000000000..480cd2d1bb54
--- /dev/null
+++ b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarFooter/CollectionSidebarFooter.styled.js
@@ -0,0 +1,31 @@
+import styled from "styled-components";
+import { Box } from "grid-styled";
+
+import { color } from "metabase/lib/colors";
+import { space } from "metabase/styled-components/theme";
+
+import GenericIcon from "metabase/components/Icon";
+import GenericLink from "metabase/components/Link";
+import { SIDEBAR_SPACER } from "metabase/collections/constants";
+
+export const Container = styled(Box)`
+ margin-top: auto;
+ padding-bottom: ${space(2)};
+ padding-left: ${SIDEBAR_SPACER * 2}px;
+`;
+
+export const Icon = styled(GenericIcon)`
+ margin-right: ${space(1)};
+`;
+
+export const Link = styled(GenericLink)`
+ align-items: center;
+ color: ${color("text-light")};
+ display: flex;
+ font-weight: 700;
+ margin-top: ${space(2)};
+
+ &:hover {
+ color: ${color("brand")};
+ }
+`;
diff --git a/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarFooter/CollectionSidebarFooter.unit.spec.js b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarFooter/CollectionSidebarFooter.unit.spec.js
new file mode 100644
index 000000000000..0315d4eb1cb8
--- /dev/null
+++ b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarFooter/CollectionSidebarFooter.unit.spec.js
@@ -0,0 +1,28 @@
+import React from "react";
+import "@testing-library/jest-dom/extend-expect";
+import { render, screen } from "@testing-library/react";
+
+import CollectionSidebarFooter from "./CollectionSidebarFooter";
+
+it("displays link to archive, including icon", () => {
+ render();
+
+ screen.getByText("View archive");
+ screen.getByLabelText("view_archive icon");
+});
+
+it("does not display link to other users personal collections if user is not superuser", () => {
+ render();
+
+ expect(
+ screen.queryByText("Other users' personal collections"),
+ ).not.toBeInTheDocument();
+ expect(screen.queryByLabelText("group icon")).not.toBeInTheDocument();
+});
+
+it("displays link to other users personal collections if user is superuser", () => {
+ render();
+
+ screen.getByText("Other users' personal collections");
+ screen.queryByLabelText("group icon");
+});
diff --git a/frontend/src/metabase/collections/containers/CollectionSidebar/Collections/Collections.jsx b/frontend/src/metabase/collections/containers/CollectionSidebar/Collections/Collections.jsx
new file mode 100644
index 000000000000..328aece1f88f
--- /dev/null
+++ b/frontend/src/metabase/collections/containers/CollectionSidebar/Collections/Collections.jsx
@@ -0,0 +1,65 @@
+import React from "react";
+import PropTypes from "prop-types";
+import CollectionsList from "metabase/collections/components/CollectionsList";
+import { Box } from "grid-styled";
+
+import {
+ nonPersonalOrArchivedCollection,
+ currentUserPersonalCollections as getCurrentUserPersonalCollections,
+} from "metabase/collections/utils";
+
+import { Container } from "./Collections.styled";
+
+const propTypes = {
+ collectionId: PropTypes.number,
+ currentUserId: PropTypes.number,
+ list: PropTypes.array,
+ onClose: PropTypes.func.isRequired,
+ onOpen: PropTypes.func.isRequired,
+ openCollections: PropTypes.array,
+};
+
+export default function Collections({
+ collectionId,
+ currentUserId,
+ list,
+ onClose,
+ onOpen,
+ openCollections,
+}) {
+ function filterPersonalCollections(collection) {
+ return !collection.archived;
+ }
+
+ const currentUserPersonalCollections = getCurrentUserPersonalCollections(
+ list,
+ currentUserId,
+ );
+
+ return (
+
+
+
+
+
+
+
+ );
+}
+
+Collections.propTypes = propTypes;
diff --git a/frontend/src/metabase/collections/containers/CollectionSidebar/Collections/Collections.styled.js b/frontend/src/metabase/collections/containers/CollectionSidebar/Collections/Collections.styled.js
new file mode 100644
index 000000000000..613367355a1a
--- /dev/null
+++ b/frontend/src/metabase/collections/containers/CollectionSidebar/Collections/Collections.styled.js
@@ -0,0 +1,8 @@
+import styled from "styled-components";
+import { Box } from "grid-styled";
+
+import { space } from "metabase/styled-components/theme";
+
+export const Container = styled(Box)`
+ padding-bottom: ${space(4)};
+`;
diff --git a/frontend/src/metabase/collections/containers/CollectionSidebar/Collections/Collections.unit.spec.js b/frontend/src/metabase/collections/containers/CollectionSidebar/Collections/Collections.unit.spec.js
new file mode 100644
index 000000000000..15a09616a7df
--- /dev/null
+++ b/frontend/src/metabase/collections/containers/CollectionSidebar/Collections/Collections.unit.spec.js
@@ -0,0 +1,26 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import { DragDropContextProvider } from "react-dnd";
+import HTML5Backend from "react-dnd-html5-backend";
+
+import Collections from "./Collections";
+
+const name = "A collection name";
+const list = [{ name }];
+
+it("displays entries", () => {
+ render(
+
+ {}}
+ onOpen={() => {}}
+ openCollections={[]}
+ />
+ ,
+ );
+
+ screen.getByText(name);
+});
diff --git a/frontend/src/metabase/collections/containers/CollectionSidebar/RootCollectionLink/RootCollectionLink.jsx b/frontend/src/metabase/collections/containers/CollectionSidebar/RootCollectionLink/RootCollectionLink.jsx
new file mode 100644
index 000000000000..ec1804eae530
--- /dev/null
+++ b/frontend/src/metabase/collections/containers/CollectionSidebar/RootCollectionLink/RootCollectionLink.jsx
@@ -0,0 +1,36 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { t } from "ttag";
+
+import * as Urls from "metabase/lib/urls";
+
+import CollectionDropTarget from "metabase/containers/dnd/CollectionDropTarget";
+import CollectionLink from "metabase/collections/components/CollectionLink";
+
+import { Container } from "./RootCollectionLink.styled";
+
+const propTypes = {
+ isRoot: PropTypes.bool.isRequired,
+ root: PropTypes.object.isRequired,
+};
+
+export default function CollectionSidebarHeader({ isRoot, root }) {
+ return (
+
+
+ {({ highlighted, hovered }) => (
+
+ {t`Our analytics`}
+
+ )}
+
+
+ );
+}
+
+CollectionSidebarHeader.propTypes = propTypes;
diff --git a/frontend/src/metabase/collections/containers/CollectionSidebar/RootCollectionLink/RootCollectionLink.styled.jsx b/frontend/src/metabase/collections/containers/CollectionSidebar/RootCollectionLink/RootCollectionLink.styled.jsx
new file mode 100644
index 000000000000..ef0b6cadc6e8
--- /dev/null
+++ b/frontend/src/metabase/collections/containers/CollectionSidebar/RootCollectionLink/RootCollectionLink.styled.jsx
@@ -0,0 +1,9 @@
+import styled from "styled-components";
+import { Box } from "grid-styled";
+
+import { space } from "metabase/styled-components/theme";
+
+export const Container = styled(Box)`
+ margin-bottom: ${space(1)};
+ margin-top: ${space(2)};
+`;
diff --git a/frontend/src/metabase/collections/containers/CollectionSidebar/RootCollectionLink/RootCollectionLink.unit.spec.js b/frontend/src/metabase/collections/containers/CollectionSidebar/RootCollectionLink/RootCollectionLink.unit.spec.js
new file mode 100644
index 000000000000..dcf62d9e2d0c
--- /dev/null
+++ b/frontend/src/metabase/collections/containers/CollectionSidebar/RootCollectionLink/RootCollectionLink.unit.spec.js
@@ -0,0 +1,18 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import { DragDropContextProvider } from "react-dnd";
+import HTML5Backend from "react-dnd-html5-backend";
+
+import RootCollectionLink from "./RootCollectionLink";
+
+it("displays link to main collection: Our Analytics", () => {
+ const root = { name: "name", id: "root" };
+
+ render(
+
+
+ ,
+ );
+
+ screen.getByText("Our analytics");
+});
From 4d62207377724e61c03c6c208b06f7df3937198d Mon Sep 17 00:00:00 2001
From: Gustavo Saiani
Date: Fri, 23 Jul 2021 09:52:12 -0300
Subject: [PATCH 088/664] Improve Collection Sidebar UI for mobile (#17163)
---
.../CollectionHeader/CollectionHeader.jsx | 12 ++++---
.../CollectionHeader.styled.js | 20 ++++++++++-
.../CollectionSidebar/CollectionSidebar.jsx | 1 -
.../CollectionSidebar.styled.js | 18 ++++++++--
.../CollectionSidebarHeader.jsx | 36 +++++++++++++++++++
.../CollectionSidebarHeader.styled.jsx | 9 +++++
.../CollectionSidebarHeader.unit.spec.js | 19 ++++++++++
7 files changed, 105 insertions(+), 10 deletions(-)
create mode 100644 frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarHeader/CollectionSidebarHeader.jsx
create mode 100644 frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarHeader/CollectionSidebarHeader.styled.jsx
create mode 100644 frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarHeader/CollectionSidebarHeader.unit.spec.js
diff --git a/frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.jsx b/frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.jsx
index efbc9c84494e..aac8c9b56e34 100644
--- a/frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.jsx
+++ b/frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.jsx
@@ -12,7 +12,9 @@ import Tooltip from "metabase/components/Tooltip";
import CollectionEditMenu from "metabase/collections/components/CollectionEditMenu";
import {
+ Container,
DescriptionTooltipIcon,
+ MenuContainer,
ToggleMobileSidebarIcon,
} from "./CollectionHeader.styled";
@@ -101,11 +103,11 @@ function CreateCollectionLink({
function Menu(props) {
return (
-
-
+
-
+
+
);
}
@@ -115,13 +117,13 @@ export default function CollectionHeader(props) {
const hasWritePermission = collection && collection.can_write;
return (
-
+
-
+
);
}
diff --git a/frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.styled.js b/frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.styled.js
index db2c00e3cae9..50ddbb4d6651 100644
--- a/frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.styled.js
+++ b/frontend/src/metabase/collections/components/CollectionHeader/CollectionHeader.styled.js
@@ -1,10 +1,28 @@
import styled from "styled-components";
-import { color } from "metabase/lib/colors";
+import { Flex } from "grid-styled";
+import { color } from "metabase/lib/colors";
import { breakpointMinSmall, space } from "metabase/styled-components/theme";
import Icon from "metabase/components/Icon";
+export const Container = styled(Flex)`
+ justify-content: space-between;
+ flex-direction: column;
+ margin-bottom: ${space(3)};
+ padding-top: ${space(0)};
+
+ ${breakpointMinSmall} {
+ align-items: center;
+ flex-direction: row;
+ padding-top: ${space(3)};
+ }
+`;
+
+export const MenuContainer = styled(Flex)`
+ margin-top: ${space(1)};
+`;
+
export const ToggleMobileSidebarIcon = styled(Icon).attrs({
name: "burger",
size: 20,
diff --git a/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebar.jsx b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebar.jsx
index 0469fadfb890..8e009d2e5e9b 100644
--- a/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebar.jsx
+++ b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebar.jsx
@@ -15,7 +15,6 @@ import {
import RootCollectionLink from "./RootCollectionLink/RootCollectionLink";
import Footer from "./CollectionSidebarFooter/CollectionSidebarFooter";
import Collections from "./Collections/Collections";
-
import LoadingSpinner from "metabase/components/LoadingSpinner";
import { getParentPath } from "metabase/collections/utils";
diff --git a/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebar.styled.js b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebar.styled.js
index af556262a0da..2069e129ad8c 100644
--- a/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebar.styled.js
+++ b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebar.styled.js
@@ -1,4 +1,4 @@
-import styled from "styled-components";
+import styled, { css } from "styled-components";
import { Box } from "grid-styled";
import { space } from "metabase/styled-components/theme";
@@ -29,7 +29,15 @@ export const Sidebar = styled(Box.withComponent("aside"))`
padding-top: ${space(3)};
position: fixed;
top: 65px;
- width: ${props => (props.shouldDisplayMobileSidebar ? "100vw" : 0)};
+ width: 0;
+
+ ${props =>
+ props.shouldDisplayMobileSidebar &&
+ css`
+ box-shadow: 5px 0px 8px rgba(0, 0, 0, 0.35),
+ 40px 0px rgba(5, 14, 31, 0.32);
+ width: calc(100vw - 40px);
+ `}
${breakpointMinSmall} {
width: ${SIDEBAR_WIDTH};
@@ -41,7 +49,11 @@ export const ToggleMobileSidebarIcon = styled(Icon).attrs({
size: 20,
})`
color: ${color("brand")};
- margin: ${space(0)} ${space(2)} 0 ${space(3)}};
+ // margin sizes hard-coded
+ // for icon to land on
+ // same position as burger icon
+ // when sidebar is hidden in mobile
+ margin: -4px ${space(2)} 0 30px};
${breakpointMinSmall} {
cursor: pointer;
diff --git a/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarHeader/CollectionSidebarHeader.jsx b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarHeader/CollectionSidebarHeader.jsx
new file mode 100644
index 000000000000..270abd2c6ebf
--- /dev/null
+++ b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarHeader/CollectionSidebarHeader.jsx
@@ -0,0 +1,36 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { t } from "ttag";
+
+import * as Urls from "metabase/lib/urls";
+
+import CollectionDropTarget from "metabase/containers/dnd/CollectionDropTarget";
+import CollectionLink from "metabase/collections/components/CollectionLink";
+
+import { Container } from "./CollectionSidebarHeader.styled";
+
+const propTypes = {
+ isRoot: PropTypes.bool.isRequired,
+ root: PropTypes.object.isRequired,
+};
+
+export default function CollectionSidebarHeader({ isRoot, root }) {
+ return (
+
+
+ {({ highlighted, hovered }) => (
+
+ {t`Our analytics`}
+
+ )}
+
+
+ );
+}
+
+CollectionSidebarHeader.propTypes = propTypes;
diff --git a/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarHeader/CollectionSidebarHeader.styled.jsx b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarHeader/CollectionSidebarHeader.styled.jsx
new file mode 100644
index 000000000000..ef0b6cadc6e8
--- /dev/null
+++ b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarHeader/CollectionSidebarHeader.styled.jsx
@@ -0,0 +1,9 @@
+import styled from "styled-components";
+import { Box } from "grid-styled";
+
+import { space } from "metabase/styled-components/theme";
+
+export const Container = styled(Box)`
+ margin-bottom: ${space(1)};
+ margin-top: ${space(2)};
+`;
diff --git a/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarHeader/CollectionSidebarHeader.unit.spec.js b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarHeader/CollectionSidebarHeader.unit.spec.js
new file mode 100644
index 000000000000..4f4395ce06a9
--- /dev/null
+++ b/frontend/src/metabase/collections/containers/CollectionSidebar/CollectionSidebarHeader/CollectionSidebarHeader.unit.spec.js
@@ -0,0 +1,19 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import { DragDropContextProvider } from "react-dnd";
+import HTML5Backend from "react-dnd-html5-backend";
+
+import CollectionSidebarHeader from "./CollectionSidebarHeader";
+
+it("displays link to main collection: Our Analytics", () => {
+ render(
+
+
+ ,
+ );
+
+ screen.getByText("Our analytics");
+});
From 33939b0cf03342d74d6555c760064c1b67712663 Mon Sep 17 00:00:00 2001
From: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Date: Fri, 23 Jul 2021 15:58:57 +0200
Subject: [PATCH 089/664] [Dashboard filters coverage] Add initial set of tests
for dashboard date filters (#17173)
---
.../e2e/helpers/e2e-dashboard-helpers.js | 11 ++
.../dashboard-filters-date.cy.spec.js | 103 ++++++++++++++++++
.../e2e-dashboard-filter-data-objects.js | 37 +++++++
3 files changed, 151 insertions(+)
create mode 100644 frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-date.cy.spec.js
create mode 100644 frontend/test/metabase/scenarios/dashboard-filters/helpers/e2e-dashboard-filter-data-objects.js
diff --git a/frontend/test/__support__/e2e/helpers/e2e-dashboard-helpers.js b/frontend/test/__support__/e2e/helpers/e2e-dashboard-helpers.js
index 29f7d8e4d9f0..1ed2d5e80d2d 100644
--- a/frontend/test/__support__/e2e/helpers/e2e-dashboard-helpers.js
+++ b/frontend/test/__support__/e2e/helpers/e2e-dashboard-helpers.js
@@ -36,3 +36,14 @@ export function checkFilterLabelAndValue(label, value) {
cy.get("fieldset").contains(value);
}
+
+export function setFilter(type, subType) {
+ cy.icon("filter").click();
+
+ cy.findByText("What do you want to filter?");
+
+ popover().within(() => {
+ cy.findByText(type).click();
+ cy.findByText(subType).click();
+ });
+}
diff --git a/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-date.cy.spec.js b/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-date.cy.spec.js
new file mode 100644
index 000000000000..ec71ebf88b27
--- /dev/null
+++ b/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-date.cy.spec.js
@@ -0,0 +1,103 @@
+import {
+ restore,
+ popover,
+ mockSessionProperty,
+ filterWidget,
+ editDashboard,
+ saveDashboard,
+ setFilter,
+} from "__support__/e2e/cypress";
+
+import { DASHBOARD_DATE_FILTERS } from "./helpers/e2e-dashboard-filter-data-objects";
+import * as DateFilter from "../native-filters/helpers/e2e-date-filter-helpers";
+
+Object.entries(DASHBOARD_DATE_FILTERS).forEach(
+ ([filter, { value, representativeResult }]) => {
+ describe(`should work for ${filter}`, () => {
+ beforeEach(() => {
+ cy.intercept("GET", "/api/table/*/query_metadata").as("metadata");
+
+ restore();
+ cy.signInAsAdmin();
+
+ mockSessionProperty("field-filter-operators-enabled?", true);
+
+ cy.visit("/dashboard/1");
+
+ editDashboard();
+ setFilter("Time", filter);
+
+ cy.findByText("Column to filter on")
+ .next("a")
+ .click();
+
+ popover()
+ .contains("Created At")
+ .first()
+ .click();
+ });
+
+ it(`should work for "${filter}" when set through the filter widget`, () => {
+ saveDashboard();
+
+ filterWidget().click();
+
+ dateFilterSelector({
+ filterType: filter,
+ filterValue: value,
+ });
+
+ cy.get(".Card").within(() => {
+ cy.findByText(representativeResult);
+ });
+ });
+
+ it(`should work for "${filter}" when set as the default filter`, () => {
+ cy.findByText("Default value")
+ .next()
+ .click();
+
+ dateFilterSelector({
+ filterType: filter,
+ filterValue: value,
+ });
+ saveDashboard();
+
+ cy.get(".Card").within(() => {
+ cy.findByText(representativeResult);
+ });
+ });
+ });
+ },
+);
+
+function dateFilterSelector({ filterType, filterValue } = {}) {
+ switch (filterType) {
+ case "Month and Year":
+ DateFilter.setMonthAndYear(filterValue);
+ break;
+
+ case "Quarter and Year":
+ DateFilter.setQuarterAndYear(filterValue);
+ break;
+
+ case "Single Date":
+ DateFilter.setSingleDate(filterValue);
+ break;
+
+ case "Date Range":
+ DateFilter.setDateRange(filterValue);
+ break;
+
+ case "Relative Date":
+ DateFilter.setRelativeDate(filterValue);
+ break;
+
+ case "All Options":
+ DateFilter.setAdHocFilter(filterValue);
+ break;
+
+ default:
+ throw new Error("Wrong filter type!");
+ }
+}
diff --git a/frontend/test/metabase/scenarios/dashboard-filters/helpers/e2e-dashboard-filter-data-objects.js b/frontend/test/metabase/scenarios/dashboard-filters/helpers/e2e-dashboard-filter-data-objects.js
new file mode 100644
index 000000000000..eda88cd81bf0
--- /dev/null
+++ b/frontend/test/metabase/scenarios/dashboard-filters/helpers/e2e-dashboard-filter-data-objects.js
@@ -0,0 +1,37 @@
+export const DASHBOARD_DATE_FILTERS = {
+ "Month and Year": {
+ value: {
+ month: "November",
+ year: "2016",
+ },
+ representativeResult: "85.88",
+ },
+ "Quarter and Year": {
+ value: {
+ quarter: "Q2",
+ year: "2016",
+ },
+ representativeResult: "44.43",
+ },
+ "Single Date": {
+ value: "15",
+ representativeResult: "No results!",
+ },
+ "Date Range": {
+ value: {
+ startDate: "13",
+ endDate: "15",
+ },
+ representativeResult: "No results!",
+ },
+ "Relative Date": {
+ value: "Past 7 days",
+ representativeResult: "No results!",
+ },
+ "All Options": {
+ value: {
+ timeBucket: "Years",
+ },
+ representativeResult: "37.65",
+ },
+};
From 3ddcb2638ccf8a4e3dcfd64e4dd32d9bcbb246ed Mon Sep 17 00:00:00 2001
From: Jeff Evans
Date: Fri, 23 Jul 2021 14:03:46 -0500
Subject: [PATCH 090/664] Add support for driver deprecation (#17028)
# Backend changes
Introducing new `superseded-by` property to plugin manifest YAML, which will indicate the driver that is to eventually replace this one (and will drive UI/UX behavior). If a driver declares this property, then it's considered to be deprecated in favor of the specified one.
Adding top level `test_modules` directory (with the same structure as modules) for the sole purpose of module/plugin testing of YAML files, which will not be included with the driver build
Updating `driver-plugin-manifest` to look for the new `test_modules` directory in addition to `modules`, when loading the driver manifest
# Frontend changes
Calculate `supersededBy` and supersedes maps from the "superseded-by" property for each engine
Change the options for the engine field to use a function to dynamically show the legacy driver if allowed by rules (either the new driver is selected, or the legacy driver was already selected for an existing DB, or the driver is not superseded by anything)
Add new `DriverWarning` component to show these warnings based on supersede status
Co-authored-by: Anton Kulyk
---
.../databases/containers/DatabaseEditApp.jsx | 14 ++-
.../DriverWarning/DriverWarning.jsx | 97 +++++++++++++++++++
.../DriverWarning/DriverWarning.styled.js | 17 ++++
.../components/DriverWarning/index.js | 1 +
.../src/metabase/entities/databases/forms.js | 44 ++++++++-
.../src/metabase/setup/components/Setup.jsx | 6 ++
src/metabase/driver.clj | 16 +++
src/metabase/driver/util.clj | 3 +-
src/metabase/plugins/initialize.clj | 2 +-
src/metabase/plugins/lazy_loaded_driver.clj | 4 +-
.../driver/driver_deprecation_test_legacy.clj | 5 +
.../driver/driver_deprecation_test_new.clj | 5 +
.../plugins/driver_deprecation_test.clj | 13 +++
.../data/driver_deprecation_test_legacy.clj | 5 +
.../test/data/driver_deprecation_test_new.clj | 5 +
test/metabase/test/initialize/plugins.clj | 21 +++-
.../resources/metabase-plugin.yaml | 16 +++
.../resources/metabase-plugin.yaml | 15 +++
18 files changed, 276 insertions(+), 13 deletions(-)
create mode 100644 frontend/src/metabase/components/DriverWarning/DriverWarning.jsx
create mode 100644 frontend/src/metabase/components/DriverWarning/DriverWarning.styled.js
create mode 100644 frontend/src/metabase/components/DriverWarning/index.js
create mode 100644 test/metabase/driver/driver_deprecation_test_legacy.clj
create mode 100644 test/metabase/driver/driver_deprecation_test_new.clj
create mode 100644 test/metabase/plugins/driver_deprecation_test.clj
create mode 100644 test/metabase/test/data/driver_deprecation_test_legacy.clj
create mode 100644 test/metabase/test/data/driver_deprecation_test_new.clj
create mode 100644 test_modules/drivers/driver-deprecation-test-legacy/resources/metabase-plugin.yaml
create mode 100644 test_modules/drivers/driver-deprecation-test-new/resources/metabase-plugin.yaml
diff --git a/frontend/src/metabase/admin/databases/containers/DatabaseEditApp.jsx b/frontend/src/metabase/admin/databases/containers/DatabaseEditApp.jsx
index 94a568d1e317..0651978624ad 100644
--- a/frontend/src/metabase/admin/databases/containers/DatabaseEditApp.jsx
+++ b/frontend/src/metabase/admin/databases/containers/DatabaseEditApp.jsx
@@ -15,6 +15,7 @@ import ActionButton from "metabase/components/ActionButton";
import AddDatabaseHelpCard from "metabase/components/AddDatabaseHelpCard";
import Button from "metabase/components/Button";
import Breadcrumbs from "metabase/components/Breadcrumbs";
+import DriverWarning from "metabase/components/DriverWarning";
import Radio from "metabase/components/Radio";
import ModalWithTrigger from "metabase/components/ModalWithTrigger";
@@ -210,15 +211,20 @@ export default class DatabaseEditApp extends Component {
)}
- {addingNewDatabase && (
-
+
+
+ {addingNewDatabase && (
-
- )}
+ )}
+
diff --git a/frontend/src/metabase/components/DriverWarning/DriverWarning.jsx b/frontend/src/metabase/components/DriverWarning/DriverWarning.jsx
new file mode 100644
index 000000000000..d91ad71189ce
--- /dev/null
+++ b/frontend/src/metabase/components/DriverWarning/DriverWarning.jsx
@@ -0,0 +1,97 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+import { t } from "ttag";
+
+import {
+ allEngines,
+ engineSupersedesMap,
+} from "metabase/entities/databases/forms";
+
+import Warnings from "metabase/query_builder/components/Warnings";
+
+import {
+ CardContent,
+ DriverWarningContainer,
+ IconContainer,
+} from "./DriverWarning.styled";
+import ExternalLink from "metabase/components/ExternalLink";
+import MetabaseSettings from "metabase/lib/settings";
+
+const propTypes = {
+ engine: PropTypes.string.isRequired,
+};
+
+const driverUpgradeHelpLink = MetabaseSettings.docsUrl(
+ "administration-guide/01-managing-databases",
+);
+
+function getSupersedesWarningContent(newDriver, supersedesDriver) {
+ return (
+
+
+ {t`This is our new ${
+ allEngines[newDriver]["driver-name"]
+ } driver, which is faster and more reliable.`}
+
+ {t`The old driver has been deprecated and will be removed in the next release. If you really
+ need to use it, you can select ${
+ allEngines[supersedesDriver]["driver-name"]
+ } now.`}
+
+ );
+}
+
+function getSupersededByWarningContent(engine) {
+ return (
+
+
+ {t`This driver has been deprecated and will be removed in the next release.`}
+
+
+ {t`We recommend that you upgrade to the new ${
+ allEngines[engine]["driver-name"]
+ } driver, which is faster and more reliable.`}
+
+
+ {t`How to upgrade a driver`}
+
+
+ );
+}
+
+function DriverWarning({ engine, ...props }) {
+ const supersededBy = engineSupersedesMap["superseded_by"][engine];
+ const supersedes = engineSupersedesMap["supersedes"][engine];
+
+ if (!supersedes && !supersededBy) {
+ return null;
+ }
+
+ const tooltipWarning = supersedes ? t`New driver` : t`Driver deprecated`;
+ const warningContent = supersedes
+ ? getSupersedesWarningContent(engine, supersedes)
+ : getSupersededByWarningContent(supersededBy);
+
+ return (
+
+
+
+
+
+ {warningContent}
+
+
+ );
+}
+
+DriverWarning.propTypes = propTypes;
+
+export default DriverWarning;
diff --git a/frontend/src/metabase/components/DriverWarning/DriverWarning.styled.js b/frontend/src/metabase/components/DriverWarning/DriverWarning.styled.js
new file mode 100644
index 000000000000..8d1f6436f2ae
--- /dev/null
+++ b/frontend/src/metabase/components/DriverWarning/DriverWarning.styled.js
@@ -0,0 +1,17 @@
+import styled from "styled-components";
+import { Flex } from "grid-styled";
+
+export const CardContent = styled(Flex)``;
+
+export const IconContainer = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+`;
+
+export const DriverWarningContainer = styled(Flex)`
+ background-color: #f9fbfb;
+ border-radius: 10px;
+ width: 300px;
+ margin-bottom: 8px;
+`;
diff --git a/frontend/src/metabase/components/DriverWarning/index.js b/frontend/src/metabase/components/DriverWarning/index.js
new file mode 100644
index 000000000000..80816f2cd180
--- /dev/null
+++ b/frontend/src/metabase/components/DriverWarning/index.js
@@ -0,0 +1 @@
+export { default } from "./DriverWarning";
diff --git a/frontend/src/metabase/entities/databases/forms.js b/frontend/src/metabase/entities/databases/forms.js
index 9da262fe59c5..f61bb41d397f 100644
--- a/frontend/src/metabase/entities/databases/forms.js
+++ b/frontend/src/metabase/entities/databases/forms.js
@@ -278,13 +278,51 @@ function getEngineFormFields(engine, details, id) {
});
}
-const ENGINE_OPTIONS = Object.entries(MetabaseSettings.get("engines") || {})
+const ENGINES = MetabaseSettings.get("engines", {});
+const ENGINE_OPTIONS = Object.entries(ENGINES)
.map(([engine, info]) => ({
name: info["driver-name"],
value: engine,
}))
.sort((a, b) => a.name.localeCompare(b.name));
+// use top level constant for engines so we only need to compute these maps once
+const ENGINE_SUPERSEDES_MAPS = Object.keys(ENGINES).reduce(
+ (acc, engine) => {
+ const newEngine = ENGINES[engine]["superseded-by"];
+ if (newEngine) {
+ acc.supersedes[newEngine] = engine;
+ acc.superseded_by[engine] = newEngine;
+ }
+ return acc;
+ },
+ { supersedes: {}, superseded_by: {} },
+);
+
+/**
+ * Returns the options to show in the engines selection widget. An engine is available to be selected if either
+ * - it is not superseded by any other engine
+ * - it is the selected engine (i.e. someone is already using it)
+ * - it is superseded by some engine, which happens to be the currently selected one
+ *
+ * The idea behind this behavior is to only show someone a "legacy" driver if they have at least selected the one that
+ * will replace it first, at which point they can "fall back" on the legacy one if needed.
+ *
+ * @param currentEngine the current (selected engine)
+ * @returns the filtered engine options to be shown in the selection widget
+ */
+function getEngineOptions(currentEngine) {
+ return ENGINE_OPTIONS.filter(engine => {
+ const engineName = engine.value;
+ const newDriver = ENGINE_SUPERSEDES_MAPS["superseded_by"][engineName];
+ return (
+ typeof newDriver === "undefined" ||
+ newDriver === currentEngine ||
+ engineName === currentEngine
+ );
+ });
+}
+
const forms = {
details: {
fields: ({ id, engine, details = {} } = {}) => [
@@ -292,7 +330,7 @@ const forms = {
name: "engine",
title: t`Database type`,
type: "select",
- options: ENGINE_OPTIONS,
+ options: getEngineOptions(engine),
placeholder: t`Select a database`,
},
{
@@ -382,3 +420,5 @@ const SCHEDULING_FIELDS = new Set([
]);
export default forms;
+export const engineSupersedesMap = ENGINE_SUPERSEDES_MAPS;
+export const allEngines = ENGINES;
diff --git a/frontend/src/metabase/setup/components/Setup.jsx b/frontend/src/metabase/setup/components/Setup.jsx
index 18ec4319a8e9..ee7057acf7b5 100644
--- a/frontend/src/metabase/setup/components/Setup.jsx
+++ b/frontend/src/metabase/setup/components/Setup.jsx
@@ -8,6 +8,7 @@ import MetabaseAnalytics from "metabase/lib/analytics";
import MetabaseSettings from "metabase/lib/settings";
import AddDatabaseHelpCard from "metabase/components/AddDatabaseHelpCard";
+import DriverWarning from "metabase/components/DriverWarning";
import ExternalLink from "metabase/components/ExternalLink";
import LogoIcon from "metabase/components/LogoIcon";
import NewsletterForm from "metabase/components/NewsletterForm";
@@ -203,6 +204,11 @@ export default class Setup extends Component {
+
`connection-properties` on the FE as well?
[driver {:details-fields props
- :driver-name (driver/display-name driver)}])))
+ :driver-name (driver/display-name driver)
+ :superseded-by (driver/superseded-by driver)}])))
;;; +----------------------------------------------------------------------------------------------------------------+
;;; | TLS Helpers |
diff --git a/src/metabase/plugins/initialize.clj b/src/metabase/plugins/initialize.clj
index 29bc6512ac9e..666e5239a1cf 100644
--- a/src/metabase/plugins/initialize.clj
+++ b/src/metabase/plugins/initialize.clj
@@ -45,7 +45,7 @@
(@initialized-plugin-names plugin-name))
(s/defn init-plugin-with-info!
- "Initiaize plugin using parsed info from a plugin maifest. Returns truthy if plugin was successfully initialized;
+ "Initialize plugin using parsed info from a plugin manifest. Returns truthy if plugin was successfully initialized;
falsey otherwise."
[info :- {:info {:name s/Str, :version s/Str, s/Keyword s/Any}
s/Keyword s/Any}]
diff --git a/src/metabase/plugins/lazy_loaded_driver.clj b/src/metabase/plugins/lazy_loaded_driver.clj
index 8a519fcbc090..bae3c48a56f9 100644
--- a/src/metabase/plugins/lazy_loaded_driver.clj
+++ b/src/metabase/plugins/lazy_loaded_driver.clj
@@ -63,6 +63,7 @@
"Register a basic shell of a Metabase driver using the information from its Metabase plugin"
[{:keys [add-to-classpath!]
init-steps :init
+ superseded-by :superseded-by
{driver-name :name, :keys [abstract display-name parent], :or {abstract false}, :as driver-info} :driver}]
{:pre [(map? driver-info)]}
(let [driver (keyword driver-name)
@@ -81,7 +82,8 @@
(doseq [[^MultiFn multifn, f]
{driver/initialize! (make-initialize! driver add-to-classpath! init-steps)
driver/display-name (when display-name (constantly display-name))
- driver/connection-properties (constantly connection-props)}]
+ driver/connection-properties (constantly connection-props)
+ driver/superseded-by (constantly (keyword superseded-by))}]
(when f
(.addMethod multifn driver f)))
;; finally, register the Metabase driver
diff --git a/test/metabase/driver/driver_deprecation_test_legacy.clj b/test/metabase/driver/driver_deprecation_test_legacy.clj
new file mode 100644
index 000000000000..fe7aebbc0a72
--- /dev/null
+++ b/test/metabase/driver/driver_deprecation_test_legacy.clj
@@ -0,0 +1,5 @@
+(ns metabase.driver.driver-deprecation-test-legacy
+ "Dummy driver for driver deprecation testing (legacy driver)"
+ (:require [metabase.driver :as driver]))
+
+(driver/register! :driver-deprecation-test-legacy, :parent :sql)
diff --git a/test/metabase/driver/driver_deprecation_test_new.clj b/test/metabase/driver/driver_deprecation_test_new.clj
new file mode 100644
index 000000000000..78ea7113cd7f
--- /dev/null
+++ b/test/metabase/driver/driver_deprecation_test_new.clj
@@ -0,0 +1,5 @@
+(ns metabase.driver.driver-deprecation-test-new
+ "Dummy driver for driver deprecation testing (new driver)"
+ (:require [metabase.driver :as driver]))
+
+(driver/register! :driver-deprecation-test-new, :parent :sql)
diff --git a/test/metabase/plugins/driver_deprecation_test.clj b/test/metabase/plugins/driver_deprecation_test.clj
new file mode 100644
index 000000000000..e3c2da63cb4b
--- /dev/null
+++ b/test/metabase/plugins/driver_deprecation_test.clj
@@ -0,0 +1,13 @@
+(ns metabase.plugins.driver-deprecation-test
+ (:require [clojure.test :refer :all]
+ [metabase.models.setting :as setting]
+ [metabase.test :as mt]
+ [metabase.test.fixtures :as fixtures]
+ [metabase.util.i18n :as i18n :refer [tru]]))
+
+(use-fixtures :once (fixtures/initialize :plugins))
+
+(deftest driver-deprecation-test
+ (mt/test-driver :driver-deprecation-test-legacy
+ (is (= :driver-deprecation-test-new
+ (get-in (setting/properties :public) [:engines :driver-deprecation-test-legacy :superseded-by])))))
diff --git a/test/metabase/test/data/driver_deprecation_test_legacy.clj b/test/metabase/test/data/driver_deprecation_test_legacy.clj
new file mode 100644
index 000000000000..4d849b095ecb
--- /dev/null
+++ b/test/metabase/test/data/driver_deprecation_test_legacy.clj
@@ -0,0 +1,5 @@
+(ns metabase.test.data.driver-deprecation-test-legacy
+ "Dummy namespace for driver deprecation testing \"legacy\" driver, test data."
+ (:require [metabase.test.data.sql :as sql.tx]))
+
+(sql.tx/add-test-extensions! :driver-deprecation-test-legacy)
diff --git a/test/metabase/test/data/driver_deprecation_test_new.clj b/test/metabase/test/data/driver_deprecation_test_new.clj
new file mode 100644
index 000000000000..8ad92d734e03
--- /dev/null
+++ b/test/metabase/test/data/driver_deprecation_test_new.clj
@@ -0,0 +1,5 @@
+(ns metabase.test.data.driver-deprecation-test-new
+ "Dummy namespace for driver deprecation testing \"new\" driver, test data."
+ (:require [metabase.test.data.sql :as sql.tx]))
+
+(sql.tx/add-test-extensions! :driver-deprecation-test-new)
diff --git a/test/metabase/test/initialize/plugins.clj b/test/metabase/test/initialize/plugins.clj
index 5794978769a0..eca64f5104b4 100644
--- a/test/metabase/test/initialize/plugins.clj
+++ b/test/metabase/test/initialize/plugins.clj
@@ -8,9 +8,23 @@
[yaml.core :as yaml]))
(defn- driver-plugin-manifest [driver]
- (let [manifest (io/file (format "modules/drivers/%s/resources/metabase-plugin.yaml" (name driver)))]
- (when (.exists manifest)
- (yaml/parse-string (slurp manifest)))))
+ (let [nm (name driver)
+ paths (mapv
+ #(format "%s/drivers/%s/resources/metabase-plugin.yaml" % nm)
+ ;; look for driver definition in both the regular modules directory, as well as in a top-level
+ ;; test_modules directory, specifically designed for test driver definitions
+ ["modules" "test_modules"])]
+ (first (filter some?
+ (for [path paths
+ :let [manifest (io/file path)]
+ :when (.exists manifest)]
+ (do
+ (println (u/format-color
+ 'green
+ "Loading plugin manifest (from %s) for driver as if it were a real plugin: %s"
+ path
+ nm))
+ (yaml/parse-string (slurp manifest))))))))
(defn- driver-parents [driver]
(let [parents-file (io/file (format "modules/drivers/%s/parents" (name driver)))]
@@ -31,7 +45,6 @@
(doseq [driver drivers
:let [info (driver-plugin-manifest driver)]
:when info]
- (println (u/format-color 'green "Loading plugin manifest for driver as if it were a real plugin: %s" driver))
(plugins.init/init-plugin-with-info! info)
;; ok, now we need to make sure we load any depenencies for those drivers as well (!)
(load-plugin-manifests! (driver-parents driver)))))
diff --git a/test_modules/drivers/driver-deprecation-test-legacy/resources/metabase-plugin.yaml b/test_modules/drivers/driver-deprecation-test-legacy/resources/metabase-plugin.yaml
new file mode 100644
index 000000000000..0bbb3173bbc9
--- /dev/null
+++ b/test_modules/drivers/driver-deprecation-test-legacy/resources/metabase-plugin.yaml
@@ -0,0 +1,16 @@
+info:
+ name: Legacy Driver
+ version: 0.0.0-SNAPSHOT
+ description: Test plugin for driver deprecation (legacy)
+driver:
+ name: driver-deprecation-test-legacy
+ display-name: Driver Deprecation Test Plugin (Legacy)
+ lazy-load: true
+ parent: sql
+ connection-properties:
+ - host
+ - port
+superseded-by: driver-deprecation-test-new
+init:
+ - step: load-namespace
+ namespace: metabase.driver.driver-deprecation-test-legacy
diff --git a/test_modules/drivers/driver-deprecation-test-new/resources/metabase-plugin.yaml b/test_modules/drivers/driver-deprecation-test-new/resources/metabase-plugin.yaml
new file mode 100644
index 000000000000..201a7981b3ee
--- /dev/null
+++ b/test_modules/drivers/driver-deprecation-test-new/resources/metabase-plugin.yaml
@@ -0,0 +1,15 @@
+info:
+ name: New Driver
+ version: 0.0.0-SNAPSHOT
+ description: Test plugin for driver deprecation (new)
+driver:
+ name: driver-deprecation-test-new
+ display-name: Driver Deprecation Test Plugin (New)
+ lazy-load: true
+ parent: sql
+ connection-properties:
+ - host
+ - port
+init:
+ - step: load-namespace
+ namespace: metabase.driver.driver-deprecation-test-new
From 35ccbbead101e51efd389d3b30e7335d3a97f576 Mon Sep 17 00:00:00 2001
From: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Date: Fri, 23 Jul 2021 21:17:12 +0200
Subject: [PATCH 091/664] [Dashboard filters coverage] Add initial set of tests
for dashboard ID filter (#17190)
---
.../e2e/helpers/e2e-dashboard-helpers.js | 5 +-
.../dashboard-filters-id.cy.spec.js | 133 ++++++++++++++++++
2 files changed, 137 insertions(+), 1 deletion(-)
create mode 100644 frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-id.cy.spec.js
diff --git a/frontend/test/__support__/e2e/helpers/e2e-dashboard-helpers.js b/frontend/test/__support__/e2e/helpers/e2e-dashboard-helpers.js
index 1ed2d5e80d2d..5621fa242aea 100644
--- a/frontend/test/__support__/e2e/helpers/e2e-dashboard-helpers.js
+++ b/frontend/test/__support__/e2e/helpers/e2e-dashboard-helpers.js
@@ -44,6 +44,9 @@ export function setFilter(type, subType) {
popover().within(() => {
cy.findByText(type).click();
- cy.findByText(subType).click();
+
+ if (subType) {
+ cy.findByText(subType).click();
+ }
});
}
diff --git a/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-id.cy.spec.js b/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-id.cy.spec.js
new file mode 100644
index 000000000000..f6f1db8c2ced
--- /dev/null
+++ b/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-id.cy.spec.js
@@ -0,0 +1,133 @@
+import {
+ restore,
+ popover,
+ mockSessionProperty,
+ filterWidget,
+ editDashboard,
+ saveDashboard,
+ setFilter,
+ checkFilterLabelAndValue,
+} from "__support__/e2e/cypress";
+
+import { addWidgetStringFilter } from "../native-filters/helpers/e2e-field-filter-helpers";
+
+describe("scenarios > dashboard > filters > ID", () => {
+ beforeEach(() => {
+ restore();
+ cy.signInAsAdmin();
+
+ mockSessionProperty("field-filter-operators-enabled?", true);
+
+ cy.visit("/dashboard/1");
+
+ editDashboard();
+ setFilter("ID");
+
+ cy.findByText("Column to filter on")
+ .next("a")
+ .click();
+ });
+
+ describe("should work for the primary key", () => {
+ beforeEach(() => {
+ popover()
+ .contains("ID")
+ .first()
+ .click();
+ });
+
+ it("when set through the filter widget", () => {
+ saveDashboard();
+
+ filterWidget().click();
+ addWidgetStringFilter("15");
+
+ cy.get(".Card").within(() => {
+ cy.findByText("114.42");
+ });
+ });
+
+ it("when set as the default filter", () => {
+ cy.findByText("Default value")
+ .next()
+ .click();
+ addWidgetStringFilter("15");
+
+ saveDashboard();
+
+ cy.get(".Card").within(() => {
+ cy.findByText("114.42");
+ });
+ });
+ });
+
+ describe("should work for the foreign key", () => {
+ beforeEach(() => {
+ popover()
+ .contains("User ID")
+ .click();
+ });
+
+ it("when set through the filter widget", () => {
+ saveDashboard();
+
+ filterWidget().click();
+ addWidgetStringFilter("4");
+
+ cy.get(".Card").within(() => {
+ cy.findByText("47.68");
+ });
+
+ checkFilterLabelAndValue("ID", "Arnold Adams - 4");
+ });
+
+ it("when set as the default filter", () => {
+ cy.findByText("Default value")
+ .next()
+ .click();
+ addWidgetStringFilter("4");
+
+ saveDashboard();
+
+ cy.get(".Card").within(() => {
+ cy.findByText("47.68");
+ });
+
+ checkFilterLabelAndValue("ID", "Arnold Adams - 4");
+ });
+ });
+
+ describe("should work on the implicit join", () => {
+ beforeEach(() => {
+ popover().within(() => {
+ cy.findAllByText("ID")
+ .last()
+ .click();
+ });
+ });
+
+ it("when set through the filter widget", () => {
+ saveDashboard();
+
+ filterWidget().click();
+ addWidgetStringFilter("10");
+
+ cy.get(".Card").within(() => {
+ cy.findByText("6.75");
+ });
+ });
+
+ it("when set as the default filter", () => {
+ cy.findByText("Default value")
+ .next()
+ .click();
+ addWidgetStringFilter("10");
+
+ saveDashboard();
+
+ cy.get(".Card").within(() => {
+ cy.findByText("6.75");
+ });
+ });
+ });
+});
From 7748d094b71931779bde69bc27ca4e8f4d664cda Mon Sep 17 00:00:00 2001
From: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Date: Fri, 23 Jul 2021 21:17:43 +0200
Subject: [PATCH 092/664] [Dashboard filters coverage] Add initial set of tests
for dashboard number filters (#17188)
* Add initial set of tests for dashboard number filters
* Fix `describe` title for date fitlers
---
.../dashboard-filters-date.cy.spec.js | 3 +-
.../dashboard-filters-number.cy.spec.js | 65 +++++++++++++++++++
.../e2e-dashboard-filter-data-objects.js | 23 +++++++
3 files changed, 90 insertions(+), 1 deletion(-)
create mode 100644 frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-number.cy.spec.js
diff --git a/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-date.cy.spec.js b/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-date.cy.spec.js
index ec71ebf88b27..3cd285c5e76d 100644
--- a/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-date.cy.spec.js
+++ b/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-date.cy.spec.js
@@ -13,7 +13,7 @@ import * as DateFilter from "../native-filters/helpers/e2e-date-filter-helpers";
Object.entries(DASHBOARD_DATE_FILTERS).forEach(
([filter, { value, representativeResult }]) => {
- describe(`should work for ${filter}`, () => {
+ describe("scenarios > dashboard > filters > date", () => {
beforeEach(() => {
cy.intercept("GET", "/api/table/*/query_metadata").as("metadata");
@@ -61,6 +61,7 @@ Object.entries(DASHBOARD_DATE_FILTERS).forEach(
filterType: filter,
filterValue: value,
});
+
saveDashboard();
cy.get(".Card").within(() => {
diff --git a/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-number.cy.spec.js b/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-number.cy.spec.js
new file mode 100644
index 000000000000..4f98f5368aad
--- /dev/null
+++ b/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-number.cy.spec.js
@@ -0,0 +1,65 @@
+import {
+ restore,
+ popover,
+ mockSessionProperty,
+ filterWidget,
+ editDashboard,
+ saveDashboard,
+ setFilter,
+} from "__support__/e2e/cypress";
+
+import { DASHBOARD_NUMBER_FILTERS } from "./helpers/e2e-dashboard-filter-data-objects";
+import { addWidgetNumberFilter } from "../native-filters/helpers/e2e-field-filter-helpers";
+
+Object.entries(DASHBOARD_NUMBER_FILTERS).forEach(
+ ([filter, { value, representativeResult }]) => {
+ describe("scenarios > dashboard > filters > number", () => {
+ beforeEach(() => {
+ cy.intercept("GET", "/api/table/*/query_metadata").as("metadata");
+
+ restore();
+ cy.signInAsAdmin();
+
+ mockSessionProperty("field-filter-operators-enabled?", true);
+
+ cy.visit("/dashboard/1");
+
+ editDashboard();
+ setFilter("Number", filter);
+
+ cy.findByText("Column to filter on")
+ .next("a")
+ .click();
+
+ popover()
+ .contains("Tax")
+ .click();
+ });
+
+ it(`should work for "${filter}" when set through the filter widget`, () => {
+ saveDashboard();
+
+ filterWidget().click();
+ addWidgetNumberFilter(value);
+
+ cy.get(".Card").within(() => {
+ cy.findByText(representativeResult);
+ });
+ });
+
+ it(`should work for "${filter}" when set as the default filter`, () => {
+ cy.findByText("Default value")
+ .next()
+ .click();
+
+ addWidgetNumberFilter(value);
+
+ saveDashboard();
+
+ cy.get(".Card").within(() => {
+ cy.findByText(representativeResult);
+ });
+ });
+ });
+ },
+);
diff --git a/frontend/test/metabase/scenarios/dashboard-filters/helpers/e2e-dashboard-filter-data-objects.js b/frontend/test/metabase/scenarios/dashboard-filters/helpers/e2e-dashboard-filter-data-objects.js
index eda88cd81bf0..4ac0c91b87d8 100644
--- a/frontend/test/metabase/scenarios/dashboard-filters/helpers/e2e-dashboard-filter-data-objects.js
+++ b/frontend/test/metabase/scenarios/dashboard-filters/helpers/e2e-dashboard-filter-data-objects.js
@@ -35,3 +35,26 @@ export const DASHBOARD_DATE_FILTERS = {
representativeResult: "37.65",
},
};
+
+export const DASHBOARD_NUMBER_FILTERS = {
+ "Equal to": {
+ value: "2.07",
+ representativeResult: "37.65",
+ },
+ "Not equal to": {
+ value: "2.07",
+ representativeResult: "110.93",
+ },
+ Between: {
+ value: ["3", "5"],
+ representativeResult: "68.23",
+ },
+ "Greater than or equal to": {
+ value: "6.01",
+ representativeResult: "110.93",
+ },
+ "Less than or equal to": {
+ value: "2",
+ representativeResult: "29.8",
+ },
+};
From 2a51e3790607cf6bd0ca622a2528a94a051e73c7 Mon Sep 17 00:00:00 2001
From: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Date: Fri, 23 Jul 2021 21:42:56 +0200
Subject: [PATCH 093/664] [Dashboard filters coverage] Add initial set of tests
for dashboard location filters (#17191)
---
.../dashboard-filters-location.cy.spec.js | 63 +++++++++++++++++++
.../e2e-dashboard-filter-data-objects.js | 27 ++++++++
2 files changed, 90 insertions(+)
create mode 100644 frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-location.cy.spec.js
diff --git a/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-location.cy.spec.js b/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-location.cy.spec.js
new file mode 100644
index 000000000000..b0911fda3ea9
--- /dev/null
+++ b/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-location.cy.spec.js
@@ -0,0 +1,63 @@
+import {
+ restore,
+ popover,
+ mockSessionProperty,
+ filterWidget,
+ editDashboard,
+ saveDashboard,
+ setFilter,
+} from "__support__/e2e/cypress";
+
+import { DASHBOARD_LOCATION_FILTERS } from "./helpers/e2e-dashboard-filter-data-objects";
+import { addWidgetStringFilter } from "../native-filters/helpers/e2e-field-filter-helpers";
+
+Object.entries(DASHBOARD_LOCATION_FILTERS).forEach(
+ ([filter, { value, representativeResult }]) => {
+ describe("scenarios > dashboard > filters > location", () => {
+ beforeEach(() => {
+ restore();
+ cy.signInAsAdmin();
+
+ mockSessionProperty("field-filter-operators-enabled?", true);
+
+ cy.visit("/dashboard/1");
+
+ editDashboard();
+ setFilter("Location", filter);
+
+ cy.findByText("Column to filter on")
+ .next("a")
+ .click();
+
+ popover()
+ .contains("City")
+ .click();
+ });
+
+ it(`should work for "${filter}" when set through the filter widget`, () => {
+ saveDashboard();
+
+ filterWidget().click();
+ addWidgetStringFilter(value);
+
+ cy.get(".Card").within(() => {
+ cy.contains(representativeResult);
+ });
+ });
+
+ it(`should work for "${filter}" when set as the default filter`, () => {
+ cy.findByText("Default value")
+ .next()
+ .click();
+
+ addWidgetStringFilter(value);
+
+ saveDashboard();
+
+ cy.get(".Card").within(() => {
+ cy.contains(representativeResult);
+ });
+ });
+ });
+ },
+);
diff --git a/frontend/test/metabase/scenarios/dashboard-filters/helpers/e2e-dashboard-filter-data-objects.js b/frontend/test/metabase/scenarios/dashboard-filters/helpers/e2e-dashboard-filter-data-objects.js
index 4ac0c91b87d8..27afa19d0e0b 100644
--- a/frontend/test/metabase/scenarios/dashboard-filters/helpers/e2e-dashboard-filter-data-objects.js
+++ b/frontend/test/metabase/scenarios/dashboard-filters/helpers/e2e-dashboard-filter-data-objects.js
@@ -58,3 +58,30 @@ export const DASHBOARD_NUMBER_FILTERS = {
representativeResult: "29.8",
},
};
+
+export const DASHBOARD_LOCATION_FILTERS = {
+ Dropdown: {
+ value: "Abbeville",
+ representativeResult: "1510",
+ },
+ "Is not": {
+ value: "Abbeville",
+ representativeResult: "37.65",
+ },
+ Contains: {
+ value: "Abb",
+ representativeResult: "1510",
+ },
+ "Does not contain": {
+ value: "Abb",
+ representativeResult: "37.65",
+ },
+ "Starts with": {
+ value: "Abb",
+ representativeResult: "1510",
+ },
+ "Ends with": {
+ value: "y",
+ representativeResult: "115.24",
+ },
+};
From 5836393347d7cfbb36a78ec1690b1ee7b67e679f Mon Sep 17 00:00:00 2001
From: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Date: Fri, 23 Jul 2021 22:34:08 +0200
Subject: [PATCH 094/664] [Dashboard filters coverage] Add initial set of tests
for dashboard text/category filters (#17192)
---
...dashboard-filters-text-category.cy.spec.js | 63 +++++++++++++++++++
.../e2e-dashboard-filter-data-objects.js | 27 ++++++++
2 files changed, 90 insertions(+)
create mode 100644 frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-text-category.cy.spec.js
diff --git a/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-text-category.cy.spec.js b/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-text-category.cy.spec.js
new file mode 100644
index 000000000000..6ee2bab97d17
--- /dev/null
+++ b/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-text-category.cy.spec.js
@@ -0,0 +1,63 @@
+import {
+ restore,
+ popover,
+ mockSessionProperty,
+ filterWidget,
+ editDashboard,
+ saveDashboard,
+ setFilter,
+} from "__support__/e2e/cypress";
+
+import { DASHBOARD_TEXT_FILTERS } from "./helpers/e2e-dashboard-filter-data-objects";
+import { addWidgetStringFilter } from "../native-filters/helpers/e2e-field-filter-helpers";
+
+Object.entries(DASHBOARD_TEXT_FILTERS).forEach(
+ ([filter, { value, representativeResult }]) => {
+ describe("scenarios > dashboard > filters > text/category", () => {
+ beforeEach(() => {
+ restore();
+ cy.signInAsAdmin();
+
+ mockSessionProperty("field-filter-operators-enabled?", true);
+
+ cy.visit("/dashboard/1");
+
+ editDashboard();
+ setFilter("Text or Category", filter);
+
+ cy.findByText("Column to filter on")
+ .next("a")
+ .click();
+
+ popover()
+ .contains("Source")
+ .click();
+ });
+
+ it(`should work for "${filter}" when set through the filter widget`, () => {
+ saveDashboard();
+
+ filterWidget().click();
+ addWidgetStringFilter(value);
+
+ cy.get(".Card").within(() => {
+ cy.contains(representativeResult);
+ });
+ });
+
+ it(`should work for "${filter}" when set as the default filter`, () => {
+ cy.findByText("Default value")
+ .next()
+ .click();
+
+ addWidgetStringFilter(value);
+
+ saveDashboard();
+
+ cy.get(".Card").within(() => {
+ cy.contains(representativeResult);
+ });
+ });
+ });
+ },
+);
diff --git a/frontend/test/metabase/scenarios/dashboard-filters/helpers/e2e-dashboard-filter-data-objects.js b/frontend/test/metabase/scenarios/dashboard-filters/helpers/e2e-dashboard-filter-data-objects.js
index 27afa19d0e0b..2ab04f5cddc3 100644
--- a/frontend/test/metabase/scenarios/dashboard-filters/helpers/e2e-dashboard-filter-data-objects.js
+++ b/frontend/test/metabase/scenarios/dashboard-filters/helpers/e2e-dashboard-filter-data-objects.js
@@ -85,3 +85,30 @@ export const DASHBOARD_LOCATION_FILTERS = {
representativeResult: "115.24",
},
};
+
+export const DASHBOARD_TEXT_FILTERS = {
+ Dropdown: {
+ value: "Organic",
+ representativeResult: "39.58",
+ },
+ "Is not": {
+ value: "Organic",
+ representativeResult: "37.65",
+ },
+ Contains: {
+ value: "oo",
+ representativeResult: "148.23",
+ },
+ "Does not contain": {
+ value: "oo",
+ representativeResult: "37.65",
+ },
+ "Starts with": {
+ value: "A",
+ representativeResult: "85.72",
+ },
+ "Ends with": {
+ value: "e",
+ representativeResult: "47.68",
+ },
+};
From 80b46b1f75382ca3e806334f6fc01f67ed484d68 Mon Sep 17 00:00:00 2001
From: Jeff Evans
Date: Fri, 23 Jul 2021 17:03:06 -0500
Subject: [PATCH 095/664] Implement JDBC based Presto driver (#16194)
Implement JDBC based Presto driver
Adding new Presto JDBC driver using the PrestoDB JDBC driver from `https://github.com/prestodb/presto`
Marking the old Presto driver as being `superseded-by` the new one
Pulling out common Presto code into new presto-common driver (modeled after the relationship between, ex: `googleanalytics` and `google)`
Putting common QP/HoneySQL logic into the new (abstract) :presto-common driver
Updating :presto driver to extend from the new common driver and only adding HTTP/REST API related methods
Adding implementation of Presto JDBC driver, named :presto-jdbc, extending from :presto-common and :sql-jdbc
Using com.facebook.presto/presto-jdbc as underlying JDBC driver dependency (since this is explicitly for Presto clusters, as opposed to Trino)
Adapting code from the existing Presto driver where appropriate
Adding new dependency-satisfied? implementation for :env-var, to allow for a plugin to require an env var be set, and making the Presto JDBC driver depend on that (specifically: `mb-enable-presto-jdbc-driver`)
Adding CircleCI configuration to run against the newer Presto (0.254) Docker image
Adding explicit ordering in a few tests where it was missing
Fixing presto-type->base-type (timestamps were being synced as :type/Time because the regex pattern was wrong)
Add tx/format-name test implementation for :presto-jdbc to lowercase table name
Make modified test Oracle friendly
Fixing bug parsing the `[:zone :time]` case within `metabase.util.date-2.parse/parse-with-formatter`; the offset is nil so it can't be passed directly in this case, so use the `standard-offset` fn (which was moved from `date-2` to `common` to get a standard offset for that zone
Fixing more test failures by adding explicit ordering
Changing sync to check whether the driver supports foreign keys before attempting to sync those (since drivers might throw an exception if attempting to check)
Moving some common test dataset functionality between :presto and :presto-jdbc to a new test.data ns for :presto-common
Adding HoneySQL form for :count-where, since we have to explicitly give a higher precision Decimal in order for Presto to not reduce the precision in results
Put limit within subquery for `expression-using-aggregation-test` (since ordering from subquery is not guaranteed in Presto)
Adding impls for ->prepared-substitution to handle substitutions for native query params
Adding HoneySQL impls for `mod` (to do as "mod(x,y)" and `timestamp` (since it's a function with no parens to invoke) functions
Adding various `sql.qp/date` impls that use the `AT TIME ZONE` operator to account for report tz, and make bucketing tests happy
---
.circleci/config.yml | 74 ++-
.../row_level_restrictions_test.clj | 24 +-
.../serialization/load_test.clj | 2 +-
modules/drivers/presto-common/project.clj | 24 +
.../resources/metabase-plugin.yaml | 11 +
.../src/metabase/driver/presto_common.clj | 212 ++++++++
.../test/metabase/test/data/presto_common.clj | 15 +
modules/drivers/presto-jdbc/parents | 1 +
modules/drivers/presto-jdbc/project.clj | 19 +
.../resources/metabase-plugin.yaml | 37 ++
.../src/metabase/driver/presto_jdbc.clj | 477 ++++++++++++++++++
.../test/metabase/driver/presto_jdbc_test.clj | 192 +++++++
.../test/metabase/test/data/presto_jdbc.clj | 136 +++++
modules/drivers/presto/parents | 1 +
modules/drivers/presto/project.clj | 3 +-
.../presto/resources/metabase-plugin.yaml | 9 +-
.../presto/src/metabase/driver/presto.clj | 193 +------
.../presto/test/metabase/test/data/presto.clj | 35 +-
src/metabase/db/spec.clj | 11 +-
src/metabase/driver/sql/query_processor.clj | 3 +-
src/metabase/plugins/dependencies.clj | 17 +-
src/metabase/util/date_2.clj | 12 +-
src/metabase/util/date_2/common.clj | 13 +-
src/metabase/util/date_2/parse.clj | 2 +-
src/metabase/util/honeysql_extensions.clj | 8 +-
.../query_processor_test/breakout_test.clj | 3 +-
.../date_bucketing_test.clj | 28 +-
.../query_processor_test/expressions_test.clj | 6 +-
.../query_processor_test/filter_test.clj | 4 +-
.../query_processor_test/parameters_test.clj | 16 +
.../query_processor_test/timezones_test.clj | 10 +-
.../test/data/dataset_definitions.clj | 3 +-
.../dataset_definitions/office-checkins.edn | 2 +-
test/metabase/util/date_2_test.clj | 8 +
34 files changed, 1359 insertions(+), 252 deletions(-)
create mode 100644 modules/drivers/presto-common/project.clj
create mode 100644 modules/drivers/presto-common/resources/metabase-plugin.yaml
create mode 100644 modules/drivers/presto-common/src/metabase/driver/presto_common.clj
create mode 100644 modules/drivers/presto-common/test/metabase/test/data/presto_common.clj
create mode 100644 modules/drivers/presto-jdbc/parents
create mode 100644 modules/drivers/presto-jdbc/project.clj
create mode 100644 modules/drivers/presto-jdbc/resources/metabase-plugin.yaml
create mode 100644 modules/drivers/presto-jdbc/src/metabase/driver/presto_jdbc.clj
create mode 100644 modules/drivers/presto-jdbc/test/metabase/driver/presto_jdbc_test.clj
create mode 100644 modules/drivers/presto-jdbc/test/metabase/test/data/presto_jdbc.clj
create mode 100644 modules/drivers/presto/parents
diff --git a/.circleci/config.yml b/.circleci/config.yml
index dba1264c59e4..915d1bd9ab27 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -134,7 +134,7 @@ executors:
- image: metabase/ci:lein-2.9.5-clojure-1.10.3.814
- image: circleci/mongo:4.0
- presto:
+ presto-186:
working_directory: /home/circleci/metabase/metabase/
docker:
- image: metabase/ci:lein-2.9.5-clojure-1.10.3.814
@@ -145,6 +145,25 @@ executors:
# OOM sometimes with the default medium size.
resource_class: large
+ presto-jdbc-env:
+ working_directory: /home/circleci/metabase/metabase/
+ docker:
+ - image: metabase/ci:lein-2.9.5-clojure-1.10.3.814
+ - image: metabase/presto-mb-ci:latest # version 0.254
+ environment:
+ JAVA_TOOL_OPTIONS: "-Xmx2g"
+ MB_PRESTO_JDBC_TEST_CATALOG: test_data
+ MB_PRESTO_JDBC_TEST_HOST: localhost
+ MB_PRESTO_JDBC_TEST_PORT: 8443
+ MB_PRESTO_JDBC_TEST_SSL: true
+ MB_PRESTO_JDBC_TEST_USER: metabase
+ MB_PRESTO_JDBC_TEST_PASSWORD: metabase
+ MB_ENABLE_PRESTO_JDBC_DRIVER: true
+ MB_PRESTO_JDBC_TEST_ADDITIONAL_OPTIONS: >
+ SSLTrustStorePath=/tmp/cacerts-with-presto-ssl.jks&SSLTrustStorePassword=changeit
+ # (see above)
+ resource_class: large
+
sparksql:
working_directory: /home/circleci/metabase/metabase/
docker:
@@ -487,6 +506,15 @@ commands:
wget --output-document=plugins/<< parameters.dest >> ${<< parameters.source >>}
no_output_timeout: 15m
+ run-command:
+ parameters:
+ command:
+ type: string
+ steps:
+ - run:
+ name: Run command
+ command: << parameters.command >>
+
jobs:
########################################################################################################################
@@ -652,6 +680,9 @@ jobs:
before-steps:
type: steps
default: []
+ after-steps:
+ type: steps
+ default: []
description:
type: string
default: ""
@@ -675,6 +706,7 @@ jobs:
no_output_timeout: << parameters.timeout >>
- store_test_results:
path: /home/circleci/metabase/metabase/target/junit
+ - steps: << parameters.after-steps >>
test-build-scripts:
executor: clojure-and-node
@@ -793,7 +825,9 @@ jobs:
- /home/circleci/metabase/metabase/modules/drivers/googleanalytics/target
- /home/circleci/metabase/metabase/modules/drivers/mongo/target
- /home/circleci/metabase/metabase/modules/drivers/oracle/target
+ - /home/circleci/metabase/metabase/modules/drivers/presto-common/target
- /home/circleci/metabase/metabase/modules/drivers/presto/target
+ - /home/circleci/metabase/metabase/modules/drivers/presto-jdbc/target
- /home/circleci/metabase/metabase/modules/drivers/redshift/target
- /home/circleci/metabase/metabase/modules/drivers/snowflake/target
- /home/circleci/metabase/metabase/modules/drivers/sparksql/target
@@ -1093,12 +1127,48 @@ workflows:
name: be-tests-presto-ee
requires:
- be-tests-ee
- e: presto
+ e: presto-186
before-steps:
- wait-for-port:
port: 8080
driver: presto
+ - test-driver:
+ name: be-tests-presto-jdbc-ee
+ requires:
+ - be-tests-ee
+ e: presto-jdbc-env # specific env for running Presto JDBC tests (newer Presto version, SSL, etc.)
+ before-steps:
+ - wait-for-port:
+ port: 8443
+ - run:
+ name: Create temp cacerts file based on bundled JDK one
+ command: cp $JAVA_HOME/lib/security/cacerts /tmp/cacerts-with-presto-ssl.jks
+ - run:
+ name: Install openssl
+ command: apk add openssl
+ - run:
+ name: Capture Presto server self signed CA
+ command: |
+ while [[ ! -s /tmp/presto-ssl-ca.pem ]];
+ do echo "Waiting to capture SSL CA" \
+ && openssl s_client -connect localhost:8443 2>/dev/null /tmp/presto-ssl-ca.pem \
+ && sleep 1; done
+ - run:
+ name: Convert Presto CA from PEM to DER
+ command: openssl x509 -outform der -in /tmp/presto-ssl-ca.pem -out /tmp/presto-ssl-ca.der
+ - run:
+ name: Import Presto CA into temp cacerts file
+ command: |
+ keytool -noprompt -import -alias presto -keystore /tmp/cacerts-with-presto-ssl.jks \
+ -storepass changeit -file /tmp/presto-ssl-ca.der -trustcacerts
+ after-steps:
+ - run:
+ name: Capture max memory usage
+ command: cat /sys/fs/cgroup/memory/memory.max_usage_in_bytes
+ when: always
+ driver: presto-jdbc
+
- test-driver:
name: be-tests-redshift-ee
requires:
diff --git a/enterprise/backend/test/metabase_enterprise/sandbox/query_processor/middleware/row_level_restrictions_test.clj b/enterprise/backend/test/metabase_enterprise/sandbox/query_processor/middleware/row_level_restrictions_test.clj
index f3b223cc40fc..088d893eee11 100644
--- a/enterprise/backend/test/metabase_enterprise/sandbox/query_processor/middleware/row_level_restrictions_test.clj
+++ b/enterprise/backend/test/metabase_enterprise/sandbox/query_processor/middleware/row_level_restrictions_test.clj
@@ -107,7 +107,7 @@
{:query (mt/native-query
{:query
(format-honeysql
- {:select [(identifier :venues :name)]
+ {:select [(identifier :venues :id) (identifier :venues :name)]
:from [(identifier :venues)]
:order-by [(identifier :venues :id)]})})})
@@ -271,10 +271,11 @@
(run-venues-count-query)))))
(testing "Make sure that you can still use a SQL-based GTAP without needing to have SQL read perms for the Database"
- (is (= [["Red Medicine"] ["Stout Burgers & Beers"]]
- (mt/rows
+ (is (= [[1 "Red Medicine"]
+ [2 "Stout Burgers & Beers"]]
+ (mt/formatted-rows [int str]
(mt/with-gtaps {:gtaps {:venues (venue-names-native-gtap-def)}}
- (mt/run-mbql-query venues {:limit 2}))))))
+ (mt/run-mbql-query venues {:limit 2, :order-by [[:asc [:field (mt/id :venues :id)]]]}))))))
(testing (str "When no card_id is included in the GTAP, should default to a query against the table, with the GTAP "
"criteria applied")
@@ -327,10 +328,12 @@
(defn- row-level-restrictions-fk-drivers
"Drivers to test row-level restrictions against foreign keys with. Includes BigQuery, which for whatever reason does
- not normally have FK tests ran for it."
+ not normally have FK tests ran for it. Excludes Presto JDBC, because that driver does NOT support fetching foreign
+ keys from the JDBC metadata, even though we enable the feature in the UI."
[]
(cond-> (mt/normal-drivers-with-feature :nested-queries :foreign-keys)
- (@tx.env/test-drivers :bigquery) (conj :bigquery)))
+ (@tx.env/test-drivers :bigquery) (conj :bigquery)
+ true (disj :presto-jdbc)))
(deftest e2e-fks-test
(mt/test-drivers (row-level-restrictions-fk-drivers)
@@ -914,8 +917,13 @@
(mt/rows (mt/run-mbql-query orders {:limit 1})))))))))))))
(deftest pivot-query-test
- ;; sample-dataset doesn't work on Redshift yet -- see #14784
- (mt/test-drivers (disj (mt/normal-drivers-with-feature :foreign-keys :nested-queries :left-join) :redshift)
+ (mt/test-drivers (disj
+ (mt/normal-drivers-with-feature :foreign-keys :nested-queries :left-join)
+ ;; sample-dataset doesn't work on Redshift yet -- see #14784
+ :redshift
+ ;; this test relies on a FK relation between $product_id->products.category, so skip for Presto
+ ;; JDBC, because that driver doesn't support resolving FKs from the JDBC metadata
+ :presto-jdbc)
(testing "Pivot table queries should work with sandboxed users (#14969)"
(mt/dataset sample-dataset
(mt/with-gtaps {:gtaps (mt/$ids
diff --git a/enterprise/backend/test/metabase_enterprise/serialization/load_test.clj b/enterprise/backend/test/metabase_enterprise/serialization/load_test.clj
index fa14d3c38507..8b7234ed735e 100644
--- a/enterprise/backend/test/metabase_enterprise/serialization/load_test.clj
+++ b/enterprise/backend/test/metabase_enterprise/serialization/load_test.clj
@@ -265,7 +265,7 @@
;; in case it already exists
(u/ignore-exceptions
(delete-directory! dump-dir))
- (mt/test-drivers (-> (mt/normal-drivers-with-feature :basic-aggregations :binning :expressions)
+ (mt/test-drivers (-> (mt/normal-drivers-with-feature :basic-aggregations :binning :expressions :foreign-keys)
;; We will run this roundtrip test against any database supporting these features ^ except
;; certain ones for specific reasons, outlined below.
;;
diff --git a/modules/drivers/presto-common/project.clj b/modules/drivers/presto-common/project.clj
new file mode 100644
index 000000000000..6fd0ab15bb36
--- /dev/null
+++ b/modules/drivers/presto-common/project.clj
@@ -0,0 +1,24 @@
+(defproject metabase/presto-common-driver "1.0.0-SNAPSHOT"
+ :min-lein-version "2.5.0"
+
+ :description "Common code for all Presto drivers. Defines HoneySQL behavior, query generation, etc."
+
+ :aliases
+ {"install-for-building-drivers" ["with-profile" "+install-for-building-drivers" "install"]}
+
+ :profiles
+ {:provided
+ {:dependencies
+ [[org.clojure/clojure "1.10.1"]
+ [metabase-core "1.0.0-SNAPSHOT"]]}
+
+ :install-for-building-drivers
+ {:auto-clean true
+ :aot :all}
+
+ :uberjar
+ {:auto-clean true
+ :aot :all
+ :javac-options ["-target" "1.8", "-source" "1.8"]
+ :target-path "target/%s"
+ :uberjar-name "presto-common.metabase-driver.jar"}})
diff --git a/modules/drivers/presto-common/resources/metabase-plugin.yaml b/modules/drivers/presto-common/resources/metabase-plugin.yaml
new file mode 100644
index 000000000000..9d6c8c96c48c
--- /dev/null
+++ b/modules/drivers/presto-common/resources/metabase-plugin.yaml
@@ -0,0 +1,11 @@
+info:
+ name: Presto Common Driver
+ version: 1.0.0-SNAPSHOT
+ description: Common code for all Presto drivers. Defines HoneySQL behavior, query generation, etc.
+driver:
+ name: presto-common
+ abstract: true
+ lazy-load: true
+init:
+ - step: load-namespace
+ namespace: metabase.driver.presto-common
diff --git a/modules/drivers/presto-common/src/metabase/driver/presto_common.clj b/modules/drivers/presto-common/src/metabase/driver/presto_common.clj
new file mode 100644
index 000000000000..498a424e3691
--- /dev/null
+++ b/modules/drivers/presto-common/src/metabase/driver/presto_common.clj
@@ -0,0 +1,212 @@
+(ns metabase.driver.presto-common
+ "Abstract common driver for Presto. It only defines SQL generation logic and doesn't involve the transport/execution
+ mechanism for actually connecting to Presto."
+ (:require [buddy.core.codecs :as codecs]
+ [honeysql.core :as hsql]
+ [honeysql.format :as hformat]
+ [honeysql.helpers :as h]
+ [java-time :as t]
+ [metabase.driver :as driver]
+ [metabase.driver.common :as driver.common]
+ [metabase.driver.sql-jdbc.sync :as sql-jdbc.sync]
+ [metabase.driver.sql.query-processor :as sql.qp]
+ [metabase.driver.sql.util :as sql.u]
+ [metabase.driver.sql.util.unprepare :as unprepare]
+ [metabase.util :as u]
+ [metabase.util.date-2 :as u.date]
+ [metabase.util.honeysql-extensions :as hx])
+ (:import java.sql.Time
+ [java.time OffsetDateTime ZonedDateTime]))
+
+(driver/register! :presto-common, :abstract? true, :parent :sql)
+
+;;; Presto API helpers
+
+(def presto-type->base-type
+ "Function that returns a `base-type` for the given `presto-type` (can be a keyword or string)."
+ (sql-jdbc.sync/pattern-based-database-type->base-type
+ [[#"(?i)boolean" :type/Boolean]
+ [#"(?i)tinyint" :type/Integer]
+ [#"(?i)smallint" :type/Integer]
+ [#"(?i)integer" :type/Integer]
+ [#"(?i)bigint" :type/BigInteger]
+ [#"(?i)real" :type/Float]
+ [#"(?i)double" :type/Float]
+ [#"(?i)decimal.*" :type/Decimal]
+ [#"(?i)varchar.*" :type/Text]
+ [#"(?i)char.*" :type/Text]
+ [#"(?i)varbinary.*" :type/*]
+ [#"(?i)json" :type/Text] ; TODO - this should probably be Dictionary or something
+ [#"(?i)date" :type/Date]
+ [#"(?i)^timestamp$" :type/DateTime]
+ [#"(?i)^timestamp with time zone$" :type/DateTimeWithTZ]
+ [#"(?i)^time$" :type/Time]
+ [#"(?i)^time with time zone$" :type/TimeWithTZ]
+ #_[#"(?i)time.+" :type/DateTime] ; TODO - get rid of this one?
+ [#"(?i)array" :type/Array]
+ [#"(?i)map" :type/Dictionary]
+ [#"(?i)row.*" :type/*] ; TODO - again, but this time we supposedly have a schema
+ [#".*" :type/*]]))
+
+(defmethod sql.qp/add-interval-honeysql-form :presto-common
+ [_ hsql-form amount unit]
+ (hsql/call :date_add (hx/literal unit) amount hsql-form))
+
+(defn describe-catalog-sql
+ "The SHOW SCHEMAS statement that will list all schemas for the given `catalog`."
+ {:added "0.39.0"}
+ [driver catalog]
+ (str "SHOW SCHEMAS FROM " (sql.u/quote-name driver :database catalog)))
+
+(defn describe-schema-sql
+ "The SHOW TABLES statement that will list all tables for the given `catalog` and `schema`."
+ {:added "0.39.0"}
+ [driver catalog schema]
+ (str "SHOW TABLES FROM " (sql.u/quote-name driver :schema catalog schema)))
+
+(defn describe-table-sql
+ "The DESCRIBE statement that will list information about the given `table`, in the given `catalog` and schema`."
+ {:added "0.39.0"}
+ [driver catalog schema table]
+ (str "DESCRIBE " (sql.u/quote-name driver :table catalog schema table)))
+
+(def excluded-schemas
+ "The set of schemas that should be excluded when querying all schemas."
+ #{"information_schema"})
+
+(defmethod driver/db-start-of-week :presto-common
+ [_]
+ :monday)
+
+(defmethod sql.qp/cast-temporal-string [:presto-common :Coercion/YYYYMMDDHHMMSSString->Temporal]
+ [_ _coercion-strategy expr]
+ (hsql/call :date_parse expr (hx/literal "%Y%m%d%H%i%s")))
+
+(defmethod sql.qp/cast-temporal-byte [:presto-common :Coercion/YYYYMMDDHHMMSSBytes->Temporal]
+ [driver _coercion-strategy expr]
+ (sql.qp/cast-temporal-string driver :Coercion/YYYYMMDDHHMMSSString->Temporal
+ (hsql/call :from_utf8 expr)))
+
+(defmethod sql.qp/->honeysql [:presto-common Boolean]
+ [_ bool]
+ (hsql/raw (if bool "TRUE" "FALSE")))
+
+(defmethod sql.qp/->honeysql [:presto-common :time]
+ [_ [_ t]]
+ (hx/cast :time (u.date/format-sql (t/local-time t))))
+
+(defmethod sql.qp/->float :presto-common
+ [_ value]
+ (hx/cast :double value))
+
+(defmethod sql.qp/->honeysql [:presto-common :regex-match-first]
+ [driver [_ arg pattern]]
+ (hsql/call :regexp_extract (sql.qp/->honeysql driver arg) (sql.qp/->honeysql driver pattern)))
+
+(defmethod sql.qp/->honeysql [:presto-common :median]
+ [driver [_ arg]]
+ (hsql/call :approx_percentile (sql.qp/->honeysql driver arg) 0.5))
+
+(defmethod sql.qp/->honeysql [:presto-common :percentile]
+ [driver [_ arg p]]
+ (hsql/call :approx_percentile (sql.qp/->honeysql driver arg) (sql.qp/->honeysql driver p)))
+
+;; Presto mod is a function like mod(x, y) rather than an operator like x mod y
+(defmethod hformat/fn-handler (u/qualified-name ::mod)
+ [_ x y]
+ (format "mod(%s, %s)" (hformat/to-sql x) (hformat/to-sql y)))
+
+(def ^:dynamic *param-splice-style*
+ "How we should splice params into SQL (i.e. 'unprepare' the SQL). Either `:friendly` (the default) or `:paranoid`.
+ `:friendly` makes a best-effort attempt to escape strings and generate SQL that is nice to look at, but should not
+ be considered safe against all SQL injection -- use this for 'convert to SQL' functionality. `:paranoid` hex-encodes
+ strings so SQL injection is impossible; this isn't nice to look at, so use this for actually running a query."
+ :friendly)
+
+(defmethod unprepare/unprepare-value [:presto-common String]
+ [_ ^String s]
+ (case *param-splice-style*
+ :friendly (str \' (sql.u/escape-sql s :ansi) \')
+ :paranoid (format "from_utf8(from_hex('%s'))" (codecs/bytes->hex (.getBytes s "UTF-8")))))
+
+;; See https://prestodb.io/docs/current/functions/datetime.html
+
+;; This is only needed for test purposes, because some of the sample data still uses legacy types
+(defmethod unprepare/unprepare-value [:presto-common Time]
+ [driver t]
+ (unprepare/unprepare-value driver (t/local-time t)))
+
+(defmethod unprepare/unprepare-value [:presto-common OffsetDateTime]
+ [_ t]
+ (format "timestamp '%s %s %s'" (t/local-date t) (t/local-time t) (t/zone-offset t)))
+
+(defmethod unprepare/unprepare-value [:presto-common ZonedDateTime]
+ [_ t]
+ (format "timestamp '%s %s %s'" (t/local-date t) (t/local-time t) (t/zone-id t)))
+
+;;; `:sql-driver` methods
+
+(defmethod sql.qp/apply-top-level-clause [:presto-common :page]
+ [_ _ honeysql-query {{:keys [items page]} :page}]
+ (let [offset (* (dec page) items)]
+ (if (zero? offset)
+ ;; if there's no offset we can simply use limit
+ (h/limit honeysql-query items)
+ ;; if we need to do an offset we have to do nesting to generate a row number and where on that
+ (let [over-clause (format "row_number() OVER (%s)"
+ (first (hsql/format (select-keys honeysql-query [:order-by])
+ :allow-dashed-names? true
+ :quoting :ansi)))]
+ (-> (apply h/select (map last (:select honeysql-query)))
+ (h/from (h/merge-select honeysql-query [(hsql/raw over-clause) :__rownum__]))
+ (h/where [:> :__rownum__ offset])
+ (h/limit items))))))
+
+(defmethod sql.qp/date [:presto-common :default] [_ _ expr] expr)
+(defmethod sql.qp/date [:presto-common :minute] [_ _ expr] (hsql/call :date_trunc (hx/literal :minute) expr))
+(defmethod sql.qp/date [:presto-common :minute-of-hour] [_ _ expr] (hsql/call :minute expr))
+(defmethod sql.qp/date [:presto-common :hour] [_ _ expr] (hsql/call :date_trunc (hx/literal :hour) expr))
+(defmethod sql.qp/date [:presto-common :hour-of-day] [_ _ expr] (hsql/call :hour expr))
+(defmethod sql.qp/date [:presto-common :day] [_ _ expr] (hsql/call :date_trunc (hx/literal :day) expr))
+(defmethod sql.qp/date [:presto-common :day-of-month] [_ _ expr] (hsql/call :day expr))
+(defmethod sql.qp/date [:presto-common :day-of-year] [_ _ expr] (hsql/call :day_of_year expr))
+
+(defmethod sql.qp/date [:presto-common :day-of-week]
+ [driver _ expr]
+ (sql.qp/adjust-day-of-week driver (hsql/call :day_of_week expr)))
+
+(defmethod sql.qp/date [:presto-common :week]
+ [driver _ expr]
+ (sql.qp/adjust-start-of-week driver (partial hsql/call :date_trunc (hx/literal :week)) expr))
+
+(defmethod sql.qp/date [:presto-common :month] [_ _ expr] (hsql/call :date_trunc (hx/literal :month) expr))
+(defmethod sql.qp/date [:presto-common :month-of-year] [_ _ expr] (hsql/call :month expr))
+(defmethod sql.qp/date [:presto-common :quarter] [_ _ expr] (hsql/call :date_trunc (hx/literal :quarter) expr))
+(defmethod sql.qp/date [:presto-common :quarter-of-year] [_ _ expr] (hsql/call :quarter expr))
+(defmethod sql.qp/date [:presto-common :year] [_ _ expr] (hsql/call :date_trunc (hx/literal :year) expr))
+
+(defmethod sql.qp/unix-timestamp->honeysql [:presto-common :seconds]
+ [_ _ expr]
+ (hsql/call :from_unixtime expr))
+
+(defmethod driver.common/current-db-time-date-formatters :presto-common
+ [_]
+ (driver.common/create-db-time-formatters "yyyy-MM-dd'T'HH:mm:ss.SSSZ"))
+
+(defmethod driver.common/current-db-time-native-query :presto-common
+ [_]
+ "select to_iso8601(current_timestamp)")
+
+(defmethod driver/current-db-time :presto-common
+ [& args]
+ (apply driver.common/current-db-time args))
+
+(doseq [[feature supported?] {:set-timezone true
+ :basic-aggregations true
+ :standard-deviation-aggregations true
+ :expressions true
+ :native-parameters true
+ :expression-aggregations true
+ :binning true
+ :foreign-keys true}]
+ (defmethod driver/supports? [:presto-common feature] [_ _] supported?))
diff --git a/modules/drivers/presto-common/test/metabase/test/data/presto_common.clj b/modules/drivers/presto-common/test/metabase/test/data/presto_common.clj
new file mode 100644
index 000000000000..182d3278046c
--- /dev/null
+++ b/modules/drivers/presto-common/test/metabase/test/data/presto_common.clj
@@ -0,0 +1,15 @@
+(ns metabase.test.data.presto-common
+ "Common functionality for handling test datasets in Presto."
+ (:require [metabase.test.data.interface :as tx]))
+
+(defmethod tx/aggregate-column-info :presto-common
+ ([driver ag-type]
+ ((get-method tx/aggregate-column-info ::tx/test-extensions) driver ag-type))
+
+ ([driver ag-type field]
+ (merge
+ ((get-method tx/aggregate-column-info ::tx/test-extensions) driver ag-type field)
+ (when (= ag-type :sum)
+ {:base_type :type/BigInteger}))))
+
+(prefer-method tx/aggregate-column-info :presto-common ::tx/test-extensions)
diff --git a/modules/drivers/presto-jdbc/parents b/modules/drivers/presto-jdbc/parents
new file mode 100644
index 000000000000..1b4a8dec8e88
--- /dev/null
+++ b/modules/drivers/presto-jdbc/parents
@@ -0,0 +1 @@
+presto-common
diff --git a/modules/drivers/presto-jdbc/project.clj b/modules/drivers/presto-jdbc/project.clj
new file mode 100644
index 000000000000..9bd5b358a66a
--- /dev/null
+++ b/modules/drivers/presto-jdbc/project.clj
@@ -0,0 +1,19 @@
+(defproject metabase/presto-jdbc-driver "1.0.0-0.254-SNAPSHOT"
+ :min-lein-version "2.5.0"
+
+ :dependencies
+ [[com.facebook.presto/presto-jdbc "0.254"]]
+
+ :profiles
+ {:provided
+ {:dependencies
+ [[org.clojure/clojure "1.10.1"]
+ [metabase-core "1.0.0-SNAPSHOT"]
+ [metabase/presto-common-driver "1.0.0-SNAPSHOT"]]}
+
+ :uberjar
+ {:auto-clean true
+ :aot :all
+ :javac-options ["-target" "1.8", "-source" "1.8"]
+ :target-path "target/%s"
+ :uberjar-name "presto-jdbc.metabase-driver.jar"}})
diff --git a/modules/drivers/presto-jdbc/resources/metabase-plugin.yaml b/modules/drivers/presto-jdbc/resources/metabase-plugin.yaml
new file mode 100644
index 000000000000..973edfb617e3
--- /dev/null
+++ b/modules/drivers/presto-jdbc/resources/metabase-plugin.yaml
@@ -0,0 +1,37 @@
+info:
+ name: Metabase Presto JDBC Driver
+ version: 1.0.0-350-SNAPSHOT
+ description: Allows Metabase to connect to Presto databases using the Presto JDBC driver
+dependencies:
+ - plugin: Presto Common Driver
+driver:
+ name: presto-jdbc
+ display-name: Presto
+ lazy-load: true
+ parent: presto-common
+ connection-properties:
+ - host
+ - merge:
+ - port
+ - default: 8080
+ - merge:
+ - dbname
+ - name: catalog
+ placeholder: hive
+ required: false
+ - name: schema
+ required: false
+ - user
+ - password
+ - ssl
+ - merge:
+ - additional-options
+ - placeholder: "trustServerCertificate=false"
+init:
+ - step: load-namespace
+ namespace: metabase.driver.presto-common
+ - step: load-namespace
+ namespace: metabase.driver.presto-jdbc
+ - step: register-jdbc-driver
+ class: com.facebook.presto.jdbc.PrestoDriver
+
diff --git a/modules/drivers/presto-jdbc/src/metabase/driver/presto_jdbc.clj b/modules/drivers/presto-jdbc/src/metabase/driver/presto_jdbc.clj
new file mode 100644
index 000000000000..3a9cd285c197
--- /dev/null
+++ b/modules/drivers/presto-jdbc/src/metabase/driver/presto_jdbc.clj
@@ -0,0 +1,477 @@
+(ns metabase.driver.presto-jdbc
+ "Presto JDBC driver. See https://prestodb.io/docs/current/ for complete dox."
+ (:require [clojure.java.jdbc :as jdbc]
+ [clojure.set :as set]
+ [clojure.string :as str]
+ [clojure.tools.logging :as log]
+ [honeysql.core :as hsql]
+ [honeysql.format :as hformat]
+ [java-time :as t]
+ [metabase.db.spec :as db.spec]
+ [metabase.driver :as driver]
+ [metabase.driver.presto-common :as presto-common]
+ [metabase.driver.sql-jdbc.common :as sql-jdbc.common]
+ [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
+ [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
+ [metabase.driver.sql-jdbc.execute.legacy-impl :as legacy]
+ [metabase.driver.sql-jdbc.sync :as sql-jdbc.sync]
+ [metabase.driver.sql-jdbc.sync.describe-database :as sql-jdbc.describe-database]
+ [metabase.driver.sql.parameters.substitution :as sql.params.substitution]
+ [metabase.driver.sql.query-processor :as sql.qp]
+ [metabase.query-processor.timezone :as qp.timezone]
+ [metabase.util :as u]
+ [metabase.util.date-2 :as u.date]
+ [metabase.util.honeysql-extensions :as hx]
+ [metabase.util.i18n :refer [trs]])
+ (:import com.facebook.presto.jdbc.PrestoConnection
+ com.mchange.v2.c3p0.C3P0ProxyConnection
+ [java.sql Connection PreparedStatement ResultSet ResultSetMetaData Time Types]
+ [java.time LocalDateTime LocalTime OffsetDateTime OffsetTime ZonedDateTime]
+ java.time.format.DateTimeFormatter
+ [java.time.temporal ChronoField Temporal]))
+
+(driver/register! :presto-jdbc, :parent #{:presto-common :sql-jdbc ::legacy/use-legacy-classes-for-read-and-set})
+
+;;; +----------------------------------------------------------------------------------------------------------------+
+;;; | Custom HoneySQL Clause Impls |
+;;; +----------------------------------------------------------------------------------------------------------------+
+
+(def ^:private ^:const timestamp-with-time-zone-db-type "timestamp with time zone")
+
+(defmethod sql.qp/->honeysql [:presto-jdbc :log]
+ [driver [_ field]]
+ ;; recent Presto versions have a `log10` function (not `log`)
+ (hsql/call :log10 (sql.qp/->honeysql driver field)))
+
+(defmethod sql.qp/->honeysql [:presto-jdbc :count-where]
+ [driver [_ pred]]
+ ;; Presto will use the precision given here in the final expression, which chops off digits
+ ;; need to explicitly provide two digits after the decimal
+ (sql.qp/->honeysql driver [:sum-where 1.00M pred]))
+
+(defmethod sql.qp/->honeysql [:presto-jdbc :time]
+ [_ [_ t]]
+ ;; make time in UTC to avoid any interpretation by Presto in the connection (i.e. report) time zone
+ (hx/cast "time with time zone" (u.date/format-sql (t/offset-time (t/local-time t) 0))))
+
+(defmethod sql.qp/->honeysql [:presto-jdbc ZonedDateTime]
+ [_ ^ZonedDateTime t]
+ ;; use the Presto cast to `timestamp with time zone` operation to interpret in the correct TZ, regardless of
+ ;; connection zone
+ (hx/cast timestamp-with-time-zone-db-type (u.date/format-sql t)))
+
+(defmethod sql.qp/->honeysql [:presto-jdbc OffsetDateTime]
+ [_ ^OffsetDateTime t]
+ ;; use the Presto cast to `timestamp with time zone` operation to interpret in the correct TZ, regardless of
+ ;; connection zone
+ (hx/cast timestamp-with-time-zone-db-type (u.date/format-sql t)))
+
+(defrecord AtTimeZone
+ ;; record type to support applying Presto's `AT TIME ZONE` operator to an expression
+ [expr zone]
+ hformat/ToSql
+ (to-sql [_]
+ (format "%s AT TIME ZONE %s"
+ (hformat/to-sql expr)
+ (hformat/to-sql (hx/literal zone)))))
+
+(defn- in-report-zone
+ "Returns a HoneySQL form to interpret the `expr` (a temporal value) in the current report time zone, via Presto's
+ `AT TIME ZONE` operator. See https://prestodb.io/docs/current/functions/datetime.html"
+ [expr]
+ (let [report-zone (qp.timezone/report-timezone-id-if-supported :presto-jdbc)
+ ;; if the expression itself has type info, use that, or else use a parent expression's type info if defined
+ type-info (hx/type-info expr)
+ db-type (hx/type-info->db-type type-info)]
+ (if (and ;; AT TIME ZONE is only valid on these Presto types; if applied to something else (ex: `date`), then
+ ;; an error will be thrown by the query analyzer
+ (contains? #{"timestamp" "timestamp with time zone" "time" "time with time zone"} db-type)
+ ;; if one has already been set, don't do so again
+ (not (::in-report-zone? (meta expr)))
+ report-zone)
+ (-> (hx/with-database-type-info (->AtTimeZone expr report-zone) timestamp-with-time-zone-db-type)
+ (vary-meta assoc ::in-report-zone? true))
+ expr)))
+
+;; most date extraction and bucketing functions need to account for report timezone
+
+(defmethod sql.qp/date [:presto-jdbc :default]
+ [_ _ expr]
+ expr)
+
+(defmethod sql.qp/date [:presto-jdbc :minute]
+ [_ _ expr]
+ (hsql/call :date_trunc (hx/literal :minute) (in-report-zone expr)))
+
+(defmethod sql.qp/date [:presto-jdbc :minute-of-hour]
+ [_ _ expr]
+ (hsql/call :minute (in-report-zone expr)))
+
+(defmethod sql.qp/date [:presto-jdbc :hour]
+ [_ _ expr]
+ (hsql/call :date_trunc (hx/literal :hour) (in-report-zone expr)))
+
+(defmethod sql.qp/date [:presto-jdbc :hour-of-day]
+ [_ _ expr]
+ (hsql/call :hour (in-report-zone expr)))
+
+(defmethod sql.qp/date [:presto-jdbc :day]
+ [_ _ expr]
+ (hsql/call :date (in-report-zone expr)))
+
+(defmethod sql.qp/date [:presto-jdbc :day-of-week]
+ [_ _ expr]
+ (sql.qp/adjust-day-of-week :presto-jdbc (hsql/call :day_of_week (in-report-zone expr))))
+
+(defmethod sql.qp/date [:presto-jdbc :day-of-month]
+ [_ _ expr]
+ (hsql/call :day (in-report-zone expr)))
+
+(defmethod sql.qp/date [:presto-jdbc :day-of-year]
+ [_ _ expr]
+ (hsql/call :day_of_year (in-report-zone expr)))
+
+(defmethod sql.qp/date [:presto-jdbc :week]
+ [_ _ expr]
+ (sql.qp/adjust-start-of-week :presto-jdbc (partial hsql/call :date_trunc (hx/literal :week)) (in-report-zone expr)))
+
+(defmethod sql.qp/date [:presto-jdbc :month]
+ [_ _ expr]
+ (hsql/call :date_trunc (hx/literal :month) (in-report-zone expr)))
+
+(defmethod sql.qp/date [:presto-jdbc :month-of-year]
+ [_ _ expr]
+ (hsql/call :month (in-report-zone expr)))
+
+(defmethod sql.qp/date [:presto-jdbc :quarter]
+ [_ _ expr]
+ (hsql/call :date_trunc (hx/literal :quarter) (in-report-zone expr)))
+
+(defmethod sql.qp/date [:presto-jdbc :quarter-of-year]
+ [_ _ expr]
+ (hsql/call :quarter (in-report-zone expr)))
+
+(defmethod sql.qp/date [:presto-jdbc :year]
+ [_ _ expr]
+ (hsql/call :date_trunc (hx/literal :year) (in-report-zone expr)))
+
+(defmethod sql.qp/unix-timestamp->honeysql [:presto-jdbc :seconds]
+ [_ _ expr]
+ (let [report-zone (qp.timezone/report-timezone-id-if-supported :presto-jdbc)]
+ (hsql/call :from_unixtime expr (hx/literal (or report-zone "UTC")))))
+
+(defmethod sql.qp/unix-timestamp->honeysql [:presto-jdbc :milliseconds]
+ [_ _ expr]
+ ;; from_unixtime doesn't support milliseconds directly, but we can add them back in
+ (let [report-zone (qp.timezone/report-timezone-id-if-supported :presto-jdbc)
+ millis (hsql/call (u/qualified-name ::mod) expr 1000)]
+ (hsql/call :date_add
+ (hx/literal "millisecond")
+ millis
+ (hsql/call :from_unixtime (hsql/call :/ expr 1000) (hx/literal (or report-zone "UTC"))))))
+
+(defmethod sql.qp/unix-timestamp->honeysql [:presto-jdbc :microseconds]
+ [driver _ expr]
+ ;; Presto can't even represent microseconds, so convert to millis and call that version
+ (sql.qp/unix-timestamp->honeysql driver :milliseconds (hsql/call :/ expr 1000)))
+
+(defmethod sql.qp/current-datetime-honeysql-form :presto-jdbc
+ [_]
+ ;; the current_timestamp in Presto returns a `timestamp with time zone`, so this needs to be overridden
+ (hx/with-type-info :%now {::hx/database-type timestamp-with-time-zone-db-type}))
+
+(defmethod hformat/fn-handler (u/qualified-name ::mod)
+ [_ x y]
+ ;; Presto mod is a function like mod(x, y) rather than an operator like x mod y
+ (format "mod(%s, %s)" (hformat/to-sql x) (hformat/to-sql y)))
+
+;;; +----------------------------------------------------------------------------------------------------------------+
+;;; | Connectivity |
+;;; +----------------------------------------------------------------------------------------------------------------+
+
+(defn- db-name
+ "Creates a \"DB name\" for the given catalog `c` and (optional) schema `s`. If both are specified, a slash is
+ used to separate them. See examples at:
+ https://prestodb.io/docs/current/installation/jdbc.html#connecting"
+ [c s]
+ (cond
+ (str/blank? c)
+ ""
+
+ (str/blank? s)
+ c
+
+ :else
+ (str c "/" s)))
+
+(defn- jdbc-spec
+ "Creates a spec for `clojure.java.jdbc` to use for connecting to Presto via JDBC, from the given `opts`."
+ [{:keys [host port catalog schema]
+ :or {host "localhost", port 5432, catalog ""}
+ :as opts}]
+ (-> (merge
+ {:classname "com.facebook.presto.jdbc.PrestoDriver"
+ :subprotocol "presto"
+ :subname (db.spec/make-subname host port (db-name catalog schema))}
+ (dissoc opts :host :port :db :catalog :schema))
+ sql-jdbc.common/handle-additional-options))
+
+(defmethod sql-jdbc.conn/connection-details->spec :presto-jdbc
+ [_ {ssl? :ssl, :as details-map}]
+ (let [props (-> details-map
+ (update :port (fn [port]
+ (if (string? port)
+ (Integer/parseInt port)
+ port)))
+ (assoc :SSL ssl?)
+ (dissoc :ssl))]
+ (jdbc-spec props)))
+
+;;; +----------------------------------------------------------------------------------------------------------------+
+;;; | Sync |
+;;; +----------------------------------------------------------------------------------------------------------------+
+
+(defmethod sql-jdbc.sync/database-type->base-type :presto-jdbc
+ [_ field-type]
+ (presto-common/presto-type->base-type field-type))
+
+(defn- have-select-privilege?
+ "Checks whether the connected user has permission to select from the given `table-name`, in the given `schema`.
+ Adapted from the legacy Presto driver implementation."
+ [driver conn schema table-name]
+ (try
+ (let [sql (sql-jdbc.describe-database/simple-select-probe-query driver schema table-name)]
+ ;; if the query completes without throwing an Exception, we can SELECT from this table
+ (jdbc/reducible-query {:connection conn} sql)
+ true)
+ (catch Throwable _
+ false)))
+
+(defn- describe-schema
+ "Gets a set of maps for all tables in the given `catalog` and `schema`. Adapted from the legacy Presto driver
+ implementation."
+ [driver conn catalog schema]
+ (let [sql (presto-common/describe-schema-sql driver catalog schema)]
+ (log/trace (trs "Running statement in describe-schema: {0}" sql))
+ (into #{} (comp (filter (fn [{table-name :table}]
+ (have-select-privilege? driver conn schema table-name)))
+ (map (fn [{table-name :table}]
+ {:name table-name
+ :schema schema})))
+ (jdbc/reducible-query {:connection conn} sql))))
+
+(defn- all-schemas
+ "Gets a set of maps for all tables in all schemas in the given `catalog`. Adapted from the legacy Presto driver
+ implementation."
+ [driver conn catalog]
+ (let [sql (presto-common/describe-catalog-sql driver catalog)]
+ (log/trace (trs "Running statement in all-schemas: {0}" sql))
+ (into []
+ (map (fn [{:keys [schema] :as full}]
+ (when-not (contains? presto-common/excluded-schemas schema)
+ (describe-schema driver conn catalog schema))))
+ (jdbc/reducible-query {:connection conn} sql))))
+
+(defmethod driver/describe-database :presto-jdbc
+ [driver {{:keys [catalog schema] :as details} :details :as database}]
+ (with-open [conn (-> (sql-jdbc.conn/db->pooled-connection-spec database)
+ jdbc/get-connection)]
+ (let [schemas (if schema #{(describe-schema driver conn catalog schema)}
+ (all-schemas driver conn catalog))]
+ {:tables (reduce set/union schemas)})))
+
+(defmethod driver/describe-table :presto-jdbc
+ [driver {{:keys [catalog] :as details} :details :as database} {schema :schema, table-name :name}]
+ (with-open [conn (-> (sql-jdbc.conn/db->pooled-connection-spec database)
+ jdbc/get-connection)]
+ (let [sql (presto-common/describe-table-sql driver catalog schema table-name)]
+ (log/trace (trs "Running statement in describe-table: {0}" sql))
+ {:schema schema
+ :name table-name
+ :fields (into
+ #{}
+ (map-indexed (fn [idx {:keys [column type] :as col}]
+ {:name column
+ :database-type type
+ :base-type (presto-common/presto-type->base-type type)
+ :database-position idx}))
+ (jdbc/reducible-query {:connection conn} sql))})))
+
+;;; +----------------------------------------------------------------------------------------------------------------+
+;;; | sql-jdbc implementations |
+;;; +----------------------------------------------------------------------------------------------------------------+
+
+(defmethod sql-jdbc.execute/prepared-statement :presto-jdbc
+ [driver ^Connection conn ^String sql params]
+ ;; with Presto JDBC driver, result set holdability must be HOLD_CURSORS_OVER_COMMIT
+ ;; defining this method simply to omit setting the holdability
+ (let [stmt (.prepareStatement conn
+ sql
+ ResultSet/TYPE_FORWARD_ONLY
+ ResultSet/CONCUR_READ_ONLY)]
+ (try
+ (try
+ (.setFetchDirection stmt ResultSet/FETCH_FORWARD)
+ (catch Throwable e
+ (log/debug e (trs "Error setting prepared statement fetch direction to FETCH_FORWARD"))))
+ (sql-jdbc.execute/set-parameters! driver stmt params)
+ stmt
+ (catch Throwable e
+ (.close stmt)
+ (throw e)))))
+
+
+(defmethod sql-jdbc.execute/statement :presto-jdbc
+ [_ ^Connection conn]
+ ;; and similarly for statement (do not set holdability)
+ (let [stmt (.createStatement conn
+ ResultSet/TYPE_FORWARD_ONLY
+ ResultSet/CONCUR_READ_ONLY)]
+ (try
+ (.setFetchDirection stmt ResultSet/FETCH_FORWARD)
+ (catch Throwable e
+ (log/debug e (trs "Error setting statement fetch direction to FETCH_FORWARD"))))
+ stmt))
+
+(defmethod driver/can-connect? :sql-jdbc
+ [driver details]
+ (sql-jdbc.conn/can-connect? driver details))
+
+(defn- ^PrestoConnection pooled-conn->presto-conn
+ "Unwraps the C3P0 `pooled-conn` and returns the underlying `PrestoConnection` it holds."
+ [^C3P0ProxyConnection pooled-conn]
+ (.unwrap pooled-conn PrestoConnection))
+
+(defmethod sql-jdbc.execute/connection-with-timezone :presto-jdbc
+ [driver database ^String timezone-id]
+ ;; Presto supports setting the session timezone via a `PrestoConnection` instance method. Under the covers,
+ ;; this is equivalent to the `X-Presto-Time-Zone` header in the HTTP request (i.e. the `:presto` driver)
+ (let [conn (.getConnection (sql-jdbc.execute/datasource-with-diagnostic-info! driver database))
+ underlying-conn (pooled-conn->presto-conn conn)]
+ (try
+ (sql-jdbc.execute/set-best-transaction-level! driver conn)
+ (when-not (str/blank? timezone-id)
+ ;; set session time zone if defined
+ (.setTimeZoneId underlying-conn timezone-id))
+ (try
+ (.setReadOnly conn true)
+ (catch Throwable e
+ (log/debug e (trs "Error setting connection to read-only"))))
+ ;; as with statement and prepared-statement, cannot set holdability on the connection level
+ conn
+ (catch Throwable e
+ (.close conn)
+ (throw e)))))
+
+(defn- date-time->substitution [ts-str]
+ (sql.params.substitution/make-stmt-subs "from_iso8601_timestamp(?)" [ts-str]))
+
+(defmethod sql.params.substitution/->prepared-substitution [:presto-jdbc ZonedDateTime]
+ [_ ^ZonedDateTime t]
+ ;; for native query parameter substitution, in order to not conflict with the `PrestoConnection` session time zone
+ ;; (which was set via report time zone), it is necessary to use the `from_iso8601_timestamp` function on the string
+ ;; representation of the `ZonedDateTime` instance, but converted to the report time zone
+ #_(date-time->substitution (.format (t/offset-date-time (t/local-date-time t) (t/zone-offset 0)) DateTimeFormatter/ISO_OFFSET_DATE_TIME))
+ (let [report-zone (qp.timezone/report-timezone-id-if-supported :presto-jdbc)
+ ^ZonedDateTime ts (if (str/blank? report-zone) t (t/with-zone-same-instant t (t/zone-id report-zone)))]
+ ;; the `from_iso8601_timestamp` only accepts timestamps with an offset (not a zone ID), so only format with offset
+ (date-time->substitution (.format ts DateTimeFormatter/ISO_OFFSET_DATE_TIME))))
+
+(defmethod sql.params.substitution/->prepared-substitution [:presto-jdbc LocalDateTime]
+ [_ ^LocalDateTime t]
+ ;; similar to above implementation, but for `LocalDateTime`
+ ;; when Presto parses this, it will account for session (report) time zone
+ (date-time->substitution (.format t DateTimeFormatter/ISO_LOCAL_DATE_TIME)))
+
+(defmethod sql.params.substitution/->prepared-substitution [:presto-jdbc OffsetDateTime]
+ [_ ^OffsetDateTime t]
+ ;; similar to above implementation, but for `ZonedDateTime`
+ ;; when Presto parses this, it will account for session (report) time zone
+ (date-time->substitution (.format t DateTimeFormatter/ISO_OFFSET_DATE_TIME)))
+
+(defn- set-time-param
+ "Converts the given instance of `java.time.temporal`, assumed to be a time (either `LocalTime` or `OffsetTime`)
+ into a `java.sql.Time`, including milliseconds, and sets the result as a parameter of the `PreparedStatement` `ps`
+ at index `i`."
+ [^PreparedStatement ps ^Integer i ^Temporal t]
+ ;; for some reason, `java-time` can't handle passing millis to java.sql.Time, so this is the most straightforward way
+ ;; I could find to do it
+ ;; reported as https://github.com/dm3/clojure.java-time/issues/74
+ (let [millis-of-day (.get t ChronoField/MILLI_OF_DAY)]
+ (.setTime ps i (Time. millis-of-day))))
+
+(defmethod sql-jdbc.execute/set-parameter [:presto-jdbc OffsetTime]
+ [_ ^PreparedStatement ps ^Integer i t]
+ ;; necessary because `PrestoPreparedStatement` does not implement the `setTime` overload having the final `Calendar`
+ ;; param
+ (let [adjusted-tz (t/with-offset-same-instant t (t/zone-offset 0))]
+ (set-time-param ps i adjusted-tz)))
+
+(defmethod sql-jdbc.execute/set-parameter [:presto-jdbc LocalTime]
+ [_ ^PreparedStatement ps ^Integer i t]
+ ;; same rationale as above
+ (set-time-param ps i t))
+
+(defn- ^LocalTime sql-time->local-time
+ "Converts the given instance of `java.sql.Time` into a `java.time.LocalTime`, including milliseconds. Needed for
+ similar reasons as `set-time-param` above."
+ [^Time sql-time]
+ ;; Java 11 adds a simpler `ofInstant` method, but since we need to run on JDK 8, we can't use it
+ ;; https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/LocalTime.html#ofInstant(java.time.Instant,java.time.ZoneId)
+ (let [^LocalTime lt (t/local-time sql-time)
+ ^Long millis (mod (.getTime sql-time) 1000)]
+ (.with lt ChronoField/MILLI_OF_SECOND millis)))
+
+(defmethod sql-jdbc.execute/read-column-thunk [:presto-jdbc Types/TIME]
+ [_ ^ResultSet rs ^ResultSetMetaData rs-meta ^Integer i]
+ (let [type-name (.getColumnTypeName rs-meta i)
+ base-type (presto-common/presto-type->base-type type-name)
+ with-tz? (isa? base-type :type/TimeWithTZ)]
+ (fn []
+ (let [local-time (-> (.getTime rs i)
+ sql-time->local-time)]
+ ;; for both `time` and `time with time zone`, the JDBC type reported by the driver is `Types/TIME`, hence
+ ;; we also need to check the column type name to differentiate between them here
+ (if with-tz?
+ ;; even though this value is a `LocalTime`, the base-type is time with time zone, so we need to shift it back to
+ ;; the UTC (0) offset
+ (t/offset-time
+ local-time
+ (t/zone-offset 0))
+ ;; else the base-type is time without time zone, so just return the local-time value
+ local-time)))))
+
+(defn- ^PrestoConnection rs->presto-conn
+ "Returns the `PrestoConnection` associated with the given `ResultSet` `rs`."
+ [^ResultSet rs]
+ (-> (.. rs getStatement getConnection)
+ pooled-conn->presto-conn))
+
+(defmethod sql-jdbc.execute/read-column-thunk [:presto-jdbc Types/TIMESTAMP]
+ [_ ^ResultSet rset ^ResultSetMetaData rsmeta ^Integer i]
+ (let [zone (.getTimeZoneId (rs->presto-conn rset))]
+ (fn []
+ (when-let [s (.getString rset i)]
+ (when-let [t (u.date/parse s)]
+ (cond
+ (or (instance? OffsetDateTime t)
+ (instance? ZonedDateTime t))
+ (-> (t/offset-date-time t)
+ ;; tests are expecting this to be in the UTC offset, so convert to that
+ (t/with-offset-same-instant (t/zone-offset 0)))
+
+ ;; presto "helpfully" returns local results already adjusted to session time zone offset for us, e.g.
+ ;; '2021-06-15T00:00:00' becomes '2021-06-15T07:00:00' if the session timezone is US/Pacific. Undo the
+ ;; madness and convert back to UTC
+ zone
+ (-> (t/zoned-date-time t zone)
+ (u.date/with-time-zone-same-instant "UTC")
+ t/local-date-time)
+ :else
+ t))))))
+
+;;; +----------------------------------------------------------------------------------------------------------------+
+;;; | Other Driver Method Impls |
+;;; +----------------------------------------------------------------------------------------------------------------+
+
+(prefer-method driver/supports? [:presto-common :set-timezone] [:sql-jdbc :set-timezone])
diff --git a/modules/drivers/presto-jdbc/test/metabase/driver/presto_jdbc_test.clj b/modules/drivers/presto-jdbc/test/metabase/driver/presto_jdbc_test.clj
new file mode 100644
index 000000000000..17ed31f61767
--- /dev/null
+++ b/modules/drivers/presto-jdbc/test/metabase/driver/presto_jdbc_test.clj
@@ -0,0 +1,192 @@
+(ns metabase.driver.presto-jdbc-test
+ (:require [clojure.java.jdbc :as jdbc]
+ [clojure.test :refer :all]
+ [honeysql.core :as hsql]
+ [honeysql.format :as hformat]
+ [java-time :as t]
+ [metabase.db.metadata-queries :as metadata-queries]
+ [metabase.driver :as driver]
+ [metabase.driver.presto-jdbc :as presto-jdbc]
+ [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
+ [metabase.driver.sql.query-processor :as sql.qp]
+ [metabase.models.database :refer [Database]]
+ [metabase.models.field :refer [Field]]
+ [metabase.models.table :as table :refer [Table]]
+ [metabase.query-processor :as qp]
+ [metabase.sync :as sync]
+ [metabase.test :as mt]
+ [metabase.test.fixtures :as fixtures]
+ [metabase.test.util :as tu]
+ [toucan.db :as db]))
+
+(use-fixtures :once (fixtures/initialize :db))
+
+(deftest describe-database-test
+ (mt/test-driver :presto-jdbc
+ (is (= {:tables #{{:name "categories" :schema "default"}
+ {:name "venues" :schema "default"}
+ {:name "checkins" :schema "default"}
+ {:name "users" :schema "default"}}}
+ (-> (driver/describe-database :presto-jdbc (mt/db))
+ (update :tables (comp set (partial filter (comp #{"categories"
+ "venues"
+ "checkins"
+ "users"}
+ :name)))))))))
+
+(deftest describe-table-test
+ (mt/test-driver :presto-jdbc
+ (is (= {:name "venues"
+ :schema "default"
+ :fields #{{:name "name",
+ ;; for HTTP based Presto driver, this is coming back as varchar(255)
+ ;; however, for whatever reason, the DESCRIBE statement results do not return the length
+ :database-type "varchar"
+ :base-type :type/Text
+ :database-position 1}
+ {:name "latitude"
+ :database-type "double"
+ :base-type :type/Float
+ :database-position 3}
+ {:name "longitude"
+ :database-type "double"
+ :base-type :type/Float
+ :database-position 4}
+ {:name "price"
+ :database-type "integer"
+ :base-type :type/Integer
+ :database-position 5}
+ {:name "category_id"
+ :database-type "integer"
+ :base-type :type/Integer
+ :database-position 2}
+ {:name "id"
+ :database-type "integer"
+ :base-type :type/Integer
+ :database-position 0}}}
+ (driver/describe-table :presto-jdbc (mt/db) (db/select-one 'Table :id (mt/id :venues)))))))
+
+(deftest table-rows-sample-test
+ (mt/test-driver :presto-jdbc
+ (is (= [[1 "Red Medicine"]
+ [2 "Stout Burgers & Beers"]
+ [3 "The Apple Pan"]
+ [4 "Wurstküche"]
+ [5 "Brite Spot Family Restaurant"]]
+ (->> (metadata-queries/table-rows-sample (Table (mt/id :venues))
+ [(Field (mt/id :venues :id))
+ (Field (mt/id :venues :name))]
+ (constantly conj))
+ (sort-by first)
+ (take 5))))))
+
+(deftest page-test
+ (testing ":page clause"
+ (is (= {:select ["name" "id"]
+ :from [{:select [[:default.categories.name "name"]
+ [:default.categories.id "id"]
+ [(hsql/raw "row_number() OVER (ORDER BY \"default\".\"categories\".\"id\" ASC)")
+ :__rownum__]]
+ :from [:default.categories]
+ :order-by [[:default.categories.id :asc]]}]
+ :where [:> :__rownum__ 5]
+ :limit 5}
+ (sql.qp/apply-top-level-clause :presto-jdbc :page
+ {:select [[:default.categories.name "name"] [:default.categories.id "id"]]
+ :from [:default.categories]
+ :order-by [[:default.categories.id :asc]]}
+ {:page {:page 2
+ :items 5}})))))
+
+(deftest db-default-timezone-test
+ (mt/test-driver :presto-jdbc
+ (is (= "UTC"
+ (tu/db-timezone-id)))))
+
+(deftest template-tag-timezone-test
+ (mt/test-driver :presto-jdbc
+ (testing "Make sure date params work correctly when report timezones are set (#10487)"
+ (mt/with-temporary-setting-values [report-timezone "Asia/Hong_Kong"]
+ ;; the `read-column-thunk` for `Types/TIMESTAMP` always returns an `OffsetDateTime`, not a `LocalDateTime`, as
+ ;; the original Presto version of this test expected; therefore, convert the `ZonedDateTime` corresponding to
+ ;; midnight on this date (at the report TZ) to `OffsetDateTime` for comparison's sake
+ (is (= [[(-> (t/zoned-date-time 2014 8 2 0 0 0 0 (t/zone-id "Asia/Hong_Kong"))
+ t/offset-date-time
+ (t/with-offset-same-instant (t/zone-offset 0)))
+ (t/local-date 2014 8 2)]]
+ (mt/rows
+ (qp/process-query
+ {:database (mt/id)
+ :type :native
+ :middleware {:format-rows? false} ; turn off formatting so we can check the raw local date objs
+ :native {:query "SELECT {{date}}, cast({{date}} AS date)"
+ :template-tags {:date {:name "date" :display_name "Date" :type "date"}}}
+ :parameters [{:type "date/single"
+ :target ["variable" ["template-tag" "date"]]
+ :value "2014-08-02"}]}))))))))
+
+(deftest splice-strings-test
+ (mt/test-driver :presto-jdbc
+ (let [query (mt/mbql-query venues
+ {:aggregation [[:count]]
+ :filter [:= $name "wow"]})]
+ (testing "The native query returned in query results should use user-friendly splicing"
+ (is (= (str "SELECT count(*) AS \"count\" "
+ "FROM \"default\".\"venues\" "
+ "WHERE \"default\".\"venues\".\"name\" = 'wow'")
+ (:query (qp/query->native-with-spliced-params query))
+ (-> (qp/process-query query) :data :native_form :query)))))))
+
+(deftest connection-tests
+ (testing "db-name is correct in all cases"
+ (doseq [[c s expected] [[nil nil ""]
+ ["" "" ""]
+ ["my_catalog" nil "my_catalog"]
+ ["my_catalog" "" "my_catalog"]
+ ["my_catalog" "my_schema" "my_catalog/my_schema"]]]
+ (is (= expected (#'presto-jdbc/db-name c s)))))
+ (testing "jdbc-spec is correct"
+ (is (= {:classname "com.facebook.presto.jdbc.PrestoDriver"
+ :subname "//my-presto-server:1234/my_catalog?Option1=Value1&Option2=Value2"
+ :subprotocol "presto"}
+ (#'presto-jdbc/jdbc-spec {:host "my-presto-server"
+ :port 1234
+ :catalog "my_catalog"
+ :schema nil
+ :additional-options "Option1=Value1&Option2=Value2"})))))
+
+(deftest honeysql-tests
+ (testing "Complex HoneySQL conversions work as expected"
+ (testing "unix-timestamp with microsecond precision"
+ (is (= [(str "date_add('millisecond', mod((1623963256123456 / 1000), 1000),"
+ " from_unixtime(((1623963256123456 / 1000) / 1000), 'UTC'))")]
+ (-> (sql.qp/unix-timestamp->honeysql :presto-jdbc :microseconds (hsql/raw 1623963256123456))
+ (hformat/format)))))))
+
+(defn- execute-ddl! [ddl-statements]
+ (mt/with-driver :presto-jdbc
+ (let [jdbc-spec (sql-jdbc.conn/connection-details->spec :presto-jdbc (:details (mt/db)))]
+ (with-open [conn (doto (jdbc/get-connection jdbc-spec))]
+ (doseq [ddl-stmt ddl-statements]
+ (with-open [stmt (.prepareStatement conn ddl-stmt)]
+ (.executeUpdate stmt)))))))
+
+(deftest specific-schema-sync-test
+ (mt/test-driver :presto-jdbc
+ (testing "When a specific schema is designated, only that one is synced"
+ (let [s "specific_schema"
+ t "specific_table"
+ db-details (:details (mt/db))
+ with-schema (assoc db-details :schema s)]
+ (execute-ddl! [(format "DROP TABLE IF EXISTS %s.%s" s t)
+ (format "DROP SCHEMA IF EXISTS %s" s)
+ (format "CREATE SCHEMA %s" s)
+ (format "CREATE TABLE %s.%s (pk INTEGER, val1 VARCHAR(512))" s t)])
+ (mt/with-temp Database [db {:engine :presto-jdbc, :name "Temp Presto JDBC Schema DB", :details with-schema}]
+ (mt/with-db db
+ ;; same as test_data, but with schema, so should NOT pick up venues, users, etc.
+ (sync/sync-database! db)
+ (is (= [{:name t, :schema s, :db_id (mt/id)}]
+ (map #(select-keys % [:name :schema :db_id]) (db/select Table :db_id (mt/id)))))))
+ (execute-ddl! [(format "DROP TABLE %s.%s" s t)
+ (format "DROP SCHEMA %s" s)])))))
diff --git a/modules/drivers/presto-jdbc/test/metabase/test/data/presto_jdbc.clj b/modules/drivers/presto-jdbc/test/metabase/test/data/presto_jdbc.clj
new file mode 100644
index 000000000000..96c4fc9defcf
--- /dev/null
+++ b/modules/drivers/presto-jdbc/test/metabase/test/data/presto_jdbc.clj
@@ -0,0 +1,136 @@
+(ns metabase.test.data.presto-jdbc
+ "Presto JDBC driver test extensions."
+ (:require [clojure.string :as str]
+ [metabase.config :as config]
+ [metabase.connection-pool :as connection-pool]
+ [metabase.driver :as driver]
+ [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
+ [metabase.test.data.interface :as tx]
+ [metabase.test.data.presto-common]
+ [metabase.test.data.sql :as sql.tx]
+ [metabase.test.data.sql-jdbc :as sql-jdbc.tx]
+ [metabase.test.data.sql-jdbc.execute :as execute]
+ [metabase.test.data.sql-jdbc.load-data :as load-data]
+ [metabase.test.data.sql.ddl :as ddl])
+ (:import [java.sql Connection DriverManager PreparedStatement]))
+
+(sql-jdbc.tx/add-test-extensions! :presto-jdbc)
+
+;; during unit tests don't treat presto as having FK support
+(defmethod driver/supports? [:presto-jdbc :foreign-keys] [_ _] (not config/is-test?))
+
+(doseq [[base-type db-type] {:type/BigInteger "BIGINT"
+ :type/Boolean "BOOLEAN"
+ :type/Date "DATE"
+ :type/DateTime "TIMESTAMP"
+ :type/DateTimeWithTZ "TIMESTAMP WITH TIME ZONE"
+ :type/DateTimeWithZoneID "TIMESTAMP WITH TIME ZONE"
+ :type/DateTimeWithZoneOffset "TIMESTAMP WITH TIME ZONE"
+ :type/Decimal "DECIMAL"
+ :type/Float "DOUBLE"
+ :type/Integer "INTEGER"
+ :type/Text "VARCHAR"
+ :type/Time "TIME"
+ :type/TimeWithTZ "TIME WITH TIME ZONE"}]
+ (defmethod sql.tx/field-base-type->sql-type [:presto-jdbc base-type] [_ _] db-type))
+
+;; in the past, we had to manually update our Docker image and add a new catalog for every new dataset definition we
+;; added. That's insane. Just use the `test-data` catalog and put everything in that, and use
+;; `db-qualified-table-name` like everyone else.
+(def ^:private test-catalog-name "test_data")
+
+(def ^:private test-schema-name "default")
+
+(defn- dash->underscore [nm]
+ (str/replace nm #"-" "_"))
+
+(defmethod tx/dbdef->connection-details :presto-jdbc
+ [_ _ {:keys [database-name]}]
+ {:host (tx/db-test-env-var-or-throw :presto-jdbc :host "localhost")
+ :port (tx/db-test-env-var :presto-jdbc :port "8080")
+ :user (tx/db-test-env-var-or-throw :presto-jdbc :user "metabase")
+ :additional-options (tx/db-test-env-var :presto-jdbc :additional-options nil)
+ :ssl (tx/db-test-env-var :presto-jdbc :ssl "false")
+ :catalog (dash->underscore database-name)
+ :schema (tx/db-test-env-var :presto-jdbc :schema nil)})
+
+(defmethod execute/execute-sql! :presto-jdbc
+ [& args]
+ (apply execute/sequentially-execute-sql! args))
+
+(defn- load-data [dbdef tabledef]
+ ;; the JDBC driver statements fail with a cryptic status 500 error if there are too many
+ ;; parameters being set in a single statement; these numbers were arrived at empirically
+ (let [chunk-size (case (:table-name tabledef)
+ "people" 30
+ "reviews" 40
+ "orders" 30
+ "venues" 50
+ "products" 50
+ "cities" 50
+ "sightings" 50
+ "incidents" 50
+ "checkins" 25
+ "airport" 50
+ 100)
+ load-fn (load-data/make-load-data-fn load-data/load-data-add-ids
+ (partial load-data/load-data-chunked pmap chunk-size))]
+ (load-fn :presto-jdbc dbdef tabledef)))
+
+(defmethod load-data/load-data! :presto-jdbc
+ [_ dbdef tabledef]
+ (load-data dbdef tabledef))
+
+(defn- jdbc-spec->connection
+ "This is to work around some weird interplay between clojure.java.jdbc caching behavior of connections based on URL,
+ combined with the fact that the Presto driver apparently closes the connection when it closes a prepare statement.
+ Therefore, create a fresh connection from the DriverManager."
+ ^Connection [jdbc-spec]
+ (DriverManager/getConnection (format "jdbc:%s:%s" (:subprotocol jdbc-spec) (:subname jdbc-spec))
+ (connection-pool/map->properties (select-keys jdbc-spec [:user :SSL]))))
+
+(defmethod load-data/do-insert! :presto-jdbc
+ [driver spec table-identifier row-or-rows]
+ (let [statements (ddl/insert-rows-ddl-statements driver table-identifier row-or-rows)]
+ (with-open [conn (jdbc-spec->connection spec)]
+ (doseq [[^String sql & params] statements]
+ (try
+ (with-open [^PreparedStatement stmt (.prepareStatement conn sql)]
+ (sql-jdbc.execute/set-parameters! driver stmt params)
+ (let [tbl-nm ((comp last :components) (into {} table-identifier))
+ rows-affected (.executeUpdate stmt)]
+ (println (format "[%s] Inserted %d rows into %s." driver rows-affected tbl-nm))))
+ (catch Throwable e
+ (throw (ex-info (format "[%s] Error executing SQL: %s" driver (ex-message e))
+ {:driver driver, :sql sql, :params params}
+ e))))))))
+
+(defmethod sql.tx/drop-db-if-exists-sql :presto-jdbc [_ _] nil)
+(defmethod sql.tx/create-db-sql :presto-jdbc [_ _] nil)
+
+(defmethod sql.tx/qualified-name-components :presto-jdbc
+ ;; use the default schema from the in-memory connector
+ ([_ db-name] [(dash->underscore db-name) "default"])
+ ([_ db-name table-name] [(dash->underscore db-name) "default" (dash->underscore table-name)])
+ ([_ db-name table-name field-name] [(dash->underscore db-name) "default" (dash->underscore table-name) field-name]))
+
+(defmethod sql.tx/pk-sql-type :presto-jdbc
+ [_]
+ "INTEGER")
+
+(defmethod sql.tx/create-table-sql :presto-jdbc
+ [driver dbdef tabledef]
+ ;; strip out the PRIMARY KEY stuff from the CREATE TABLE statement
+ (let [sql ((get-method sql.tx/create-table-sql :sql/test-extensions) driver dbdef tabledef)]
+ (str/replace sql #", PRIMARY KEY \([^)]+\)" "")))
+
+(defmethod tx/format-name :presto-jdbc [_ table-or-field-name]
+ (dash->underscore table-or-field-name))
+
+;; Presto doesn't support FKs, at least not adding them via DDL
+(defmethod sql.tx/add-fk-sql :presto-jdbc
+ [_ _ _ _]
+ nil)
+
+;; FIXME Presto actually has very good timezone support
+#_(defmethod tx/has-questionable-timezone-support? :presto-jdbc [_] true)
diff --git a/modules/drivers/presto/parents b/modules/drivers/presto/parents
new file mode 100644
index 000000000000..1b4a8dec8e88
--- /dev/null
+++ b/modules/drivers/presto/parents
@@ -0,0 +1 @@
+presto-common
diff --git a/modules/drivers/presto/project.clj b/modules/drivers/presto/project.clj
index 037b559f2624..33c8f12c6277 100644
--- a/modules/drivers/presto/project.clj
+++ b/modules/drivers/presto/project.clj
@@ -5,7 +5,8 @@
{:provided
{:dependencies
[[org.clojure/clojure "1.10.1"]
- [metabase-core "1.0.0-SNAPSHOT"]]}
+ [metabase-core "1.0.0-SNAPSHOT"]
+ [metabase/presto-common-driver "1.0.0-SNAPSHOT"]]}
:uberjar
{:auto-clean true
diff --git a/modules/drivers/presto/resources/metabase-plugin.yaml b/modules/drivers/presto/resources/metabase-plugin.yaml
index 1dee26628cbc..859cb2460f5d 100644
--- a/modules/drivers/presto/resources/metabase-plugin.yaml
+++ b/modules/drivers/presto/resources/metabase-plugin.yaml
@@ -2,11 +2,13 @@ info:
name: Metabase Presto Driver
version: 1.0.0-SNAPSHOT
description: Allows Metabase to connect to Presto databases.
+dependencies:
+ - plugin: Presto Common Driver
driver:
name: presto
- display-name: Presto
+ display-name: Presto (Deprecated Driver)
lazy-load: true
- parent: sql
+ parent: presto-common
connection-properties:
- host
- merge:
@@ -21,5 +23,8 @@ driver:
- ssl
connection-properties-include-tunnel-config: true
init:
+ - step: load-namespace
+ namespace: metabase.driver.presto-common
- step: load-namespace
namespace: metabase.driver.presto
+superseded-by: presto-jdbc
diff --git a/modules/drivers/presto/src/metabase/driver/presto.clj b/modules/drivers/presto/src/metabase/driver/presto.clj
index b07a1ec4d0ed..96032e372837 100644
--- a/modules/drivers/presto/src/metabase/driver/presto.clj
+++ b/modules/drivers/presto/src/metabase/driver/presto.clj
@@ -1,19 +1,15 @@
(ns metabase.driver.presto
- "Presto driver. See https://prestodb.io/docs/current/ for complete dox."
- (:require [buddy.core.codecs :as codecs]
- [clj-http.client :as http]
+ "Presto driver. Executes queries via the REST API. See https://prestodb.io/docs/current/ for complete dox."
+ (:require [clj-http.client :as http]
[clojure.core.async :as a]
[clojure.set :as set]
[clojure.string :as str]
[clojure.tools.logging :as log]
- [honeysql.core :as hsql]
- [honeysql.helpers :as h]
- [java-time :as t]
[medley.core :as m]
[metabase.driver :as driver]
[metabase.driver.common :as driver.common]
+ [metabase.driver.presto-common :as presto-common]
[metabase.driver.sql-jdbc.sync.describe-database :as sql-jdbc.describe-database]
- [metabase.driver.sql.query-processor :as sql.qp]
[metabase.driver.sql.util :as sql.u]
[metabase.driver.sql.util.unprepare :as unprepare]
[metabase.query-processor.context :as context]
@@ -22,15 +18,12 @@
[metabase.query-processor.util :as qputil]
[metabase.util :as u]
[metabase.util.date-2 :as u.date]
- [metabase.util.honeysql-extensions :as hx]
[metabase.util.i18n :refer [trs tru]]
[metabase.util.schema :as su]
[metabase.util.ssh :as ssh]
- [schema.core :as s])
- (:import java.sql.Time
- [java.time OffsetDateTime ZonedDateTime]))
+ [schema.core :as s]))
-(driver/register! :presto, :parent :sql)
+(driver/register! :presto, :parent :presto-common)
;;; Presto API helpers
@@ -40,28 +33,6 @@
:catalog su/NonBlankString
s/Any s/Any})
-(defn- presto-type->base-type [field-type]
- (condp re-matches field-type
- #"boolean" :type/Boolean
- #"tinyint" :type/Integer
- #"smallint" :type/Integer
- #"integer" :type/Integer
- #"bigint" :type/BigInteger
- #"real" :type/Float
- #"double" :type/Float
- #"decimal.*" :type/Decimal
- #"varchar.*" :type/Text
- #"char.*" :type/Text
- #"varbinary.*" :type/*
- #"json" :type/Text ; TODO - this should probably be Dictionary or something
- #"date" :type/Date
- #"time" :type/Time
- #"time.+" :type/DateTime
- #"array" :type/Array
- #"map" :type/Dictionary
- #"row.*" :type/* ; TODO - again, but this time we supposedly have a schema
- #".*" :type/*))
-
(s/defn ^:private details->uri
[{:keys [ssl host port]} :- PrestoConnectionDetails, path]
(str (if ssl "https" "http") "://" host ":" port
@@ -124,7 +95,7 @@
(if (seq columns)
(for [{col-name :name, col-type :type} columns]
{:name col-name
- :base_type (presto-type->base-type col-type)})
+ :base_type (presto-common/presto-type->base-type col-type)})
(:cols @next-page)))})))
(defn- cancel-query-with-id! [details query-id info-uri]
@@ -181,7 +152,7 @@
(parse-row row))
cols (for [{col-name :name, col-type :type} columns]
{:name col-name
- :base_type (presto-type->base-type col-type)})
+ :base_type (presto-common/presto-type->base-type col-type)})
async-results (delay
(when nextUri
(fetch-results-async details canceled-chan id infoUri nextUri)))]
@@ -216,14 +187,10 @@
(sql.u/quote-name driver :database catalog)))]
(= v "information_schema")))
-(defmethod sql.qp/add-interval-honeysql-form :presto
- [_ hsql-form amount unit]
- (hsql/call :date_add (hx/literal unit) amount hsql-form))
-
(s/defn ^:private database->all-schemas :- #{su/NonBlankString}
"Return a set of all schema names in this `database`."
[driver {{:keys [catalog schema] :as details} :details :as database}]
- (let [sql (str "SHOW SCHEMAS FROM " (sql.u/quote-name driver :database catalog))
+ (let [sql (presto-common/describe-catalog-sql driver catalog)
{:keys [rows]} (execute-query-for-sync details sql)]
(set (map first rows))))
@@ -237,97 +204,30 @@
false)))
(defn- describe-schema [driver {{:keys [catalog user] :as details} :details :as db} {:keys [schema]}]
- (let [sql (str "SHOW TABLES FROM " (sql.u/quote-name driver :schema catalog schema))]
+ (let [sql (presto-common/describe-schema-sql driver catalog schema)]
(set (for [[table-name & _] (:rows (execute-query-for-sync details sql))
:when (have-select-privilege? driver details schema table-name)]
{:name table-name
:schema schema}))))
-(def ^:private excluded-schemas #{"information_schema"})
-
(defmethod driver/describe-database :presto
[driver database]
- (let [schemas (remove excluded-schemas (database->all-schemas driver database))]
+ (let [schemas (remove presto-common/excluded-schemas (database->all-schemas driver database))]
{:tables (reduce set/union (for [schema schemas]
(describe-schema driver database {:schema schema})))}))
(defmethod driver/describe-table :presto
[driver {{:keys [catalog] :as details} :details} {schema :schema, table-name :name}]
- (let [sql (str "DESCRIBE " (sql.u/quote-name driver :table catalog schema table-name))
+ (let [sql (presto-common/describe-table-sql driver catalog schema table-name)
{:keys [rows]} (execute-query-for-sync details sql)]
{:schema schema
:name table-name
:fields (set (for [[idx [name type]] (m/indexed rows)]
{:name name
:database-type type
- :base-type (presto-type->base-type type)
+ :base-type (presto-common/presto-type->base-type type)
:database-position idx}))}))
-(defmethod driver/db-start-of-week :presto
- [_]
- :monday)
-
-(defmethod sql.qp/cast-temporal-string [:presto :Coercion/YYYYMMDDHHMMSSString->Temporal]
- [driver _coercion-strategy expr]
- (hsql/call :date_parse expr (hx/literal "%Y%m%d%H%i%s")))
-
-(defmethod sql.qp/cast-temporal-byte [:presto :Coercion/YYYYMMDDHHMMSSBytes->Temporal]
- [driver _coercion-strategy expr]
- (sql.qp/cast-temporal-string driver :Coercion/YYYYMMDDHHMMSSString->Temporal
- (hsql/call :from_utf8 expr)))
-
-(defmethod sql.qp/->honeysql [:presto Boolean]
- [_ bool]
- (hsql/raw (if bool "TRUE" "FALSE")))
-
-(defmethod sql.qp/->honeysql [:presto :time]
- [_ [_ t]]
- (hx/cast :time (u.date/format-sql (t/local-time t))))
-
-(defmethod sql.qp/->float :presto
- [_ value]
- (hx/cast :double value))
-
-(defmethod sql.qp/->honeysql [:presto :regex-match-first]
- [driver [_ arg pattern]]
- (hsql/call :regexp_extract (sql.qp/->honeysql driver arg) (sql.qp/->honeysql driver pattern)))
-
-(defmethod sql.qp/->honeysql [:presto :median]
- [driver [_ arg]]
- (hsql/call :approx_percentile (sql.qp/->honeysql driver arg) 0.5))
-
-(defmethod sql.qp/->honeysql [:presto :percentile]
- [driver [_ arg p]]
- (hsql/call :approx_percentile (sql.qp/->honeysql driver arg) (sql.qp/->honeysql driver p)))
-
-(def ^:private ^:dynamic *param-splice-style*
- "How we should splice params into SQL (i.e. 'unprepare' the SQL). Either `:friendly` (the default) or `:paranoid`.
- `:friendly` makes a best-effort attempt to escape strings and generate SQL that is nice to look at, but should not
- be considered safe against all SQL injection -- use this for 'convert to SQL' functionality. `:paranoid` hex-encodes
- strings so SQL injection is impossible; this isn't nice to look at, so use this for actually running a query."
- :friendly)
-
-(defmethod unprepare/unprepare-value [:presto String]
- [_ ^String s]
- (case *param-splice-style*
- :friendly (str \' (sql.u/escape-sql s :ansi) \')
- :paranoid (format "from_utf8(from_hex('%s'))" (codecs/bytes->hex (.getBytes s "UTF-8")))))
-
-;; See https://prestodb.io/docs/current/functions/datetime.html
-
-;; This is only needed for test purposes, because some of the sample data still uses legacy types
-(defmethod unprepare/unprepare-value [:presto Time]
- [driver t]
- (unprepare/unprepare-value driver (t/local-time t)))
-
-(defmethod unprepare/unprepare-value [:presto OffsetDateTime]
- [_ t]
- (format "timestamp '%s %s %s'" (t/local-date t) (t/local-time t) (t/zone-offset t)))
-
-(defmethod unprepare/unprepare-value [:presto ZonedDateTime]
- [_ t]
- (format "timestamp '%s %s %s'" (t/local-date t) (t/local-time t) (t/zone-id t)))
-
(defmethod driver/execute-reducible-query :presto
[driver
{database-id :database
@@ -339,7 +239,7 @@
respond]
(let [sql (str "-- "
(qputil/query->remark :presto outer-query) "\n"
- (binding [*param-splice-style* :paranoid]
+ (binding [presto-common/*param-splice-style* :paranoid]
(unprepare/unprepare driver (cons sql params))))
details (merge (:details (qp.store/database))
settings)]
@@ -359,70 +259,3 @@
#".*" ; default
message))
-
-;;; `:sql-driver` methods
-
-(defmethod sql.qp/apply-top-level-clause [:presto :page]
- [_ _ honeysql-query {{:keys [items page]} :page}]
- (let [offset (* (dec page) items)]
- (if (zero? offset)
- ;; if there's no offset we can simply use limit
- (h/limit honeysql-query items)
- ;; if we need to do an offset we have to do nesting to generate a row number and where on that
- (let [over-clause (format "row_number() OVER (%s)"
- (first (hsql/format (select-keys honeysql-query [:order-by])
- :allow-dashed-names? true
- :quoting :ansi)))]
- (-> (apply h/select (map last (:select honeysql-query)))
- (h/from (h/merge-select honeysql-query [(hsql/raw over-clause) :__rownum__]))
- (h/where [:> :__rownum__ offset])
- (h/limit items))))))
-
-(defmethod sql.qp/date [:presto :default] [_ _ expr] expr)
-(defmethod sql.qp/date [:presto :minute] [_ _ expr] (hsql/call :date_trunc (hx/literal :minute) expr))
-(defmethod sql.qp/date [:presto :minute-of-hour] [_ _ expr] (hsql/call :minute expr))
-(defmethod sql.qp/date [:presto :hour] [_ _ expr] (hsql/call :date_trunc (hx/literal :hour) expr))
-(defmethod sql.qp/date [:presto :hour-of-day] [_ _ expr] (hsql/call :hour expr))
-(defmethod sql.qp/date [:presto :day] [_ _ expr] (hsql/call :date_trunc (hx/literal :day) expr))
-(defmethod sql.qp/date [:presto :day-of-month] [_ _ expr] (hsql/call :day expr))
-(defmethod sql.qp/date [:presto :day-of-year] [_ _ expr] (hsql/call :day_of_year expr))
-
-(defmethod sql.qp/date [:presto :day-of-week]
- [_ _ expr]
- (sql.qp/adjust-day-of-week :presto (hsql/call :day_of_week expr)))
-
-(defmethod sql.qp/date [:presto :week]
- [_ _ expr]
- (sql.qp/adjust-start-of-week :presto (partial hsql/call :date_trunc (hx/literal :week)) expr))
-
-(defmethod sql.qp/date [:presto :month] [_ _ expr] (hsql/call :date_trunc (hx/literal :month) expr))
-(defmethod sql.qp/date [:presto :month-of-year] [_ _ expr] (hsql/call :month expr))
-(defmethod sql.qp/date [:presto :quarter] [_ _ expr] (hsql/call :date_trunc (hx/literal :quarter) expr))
-(defmethod sql.qp/date [:presto :quarter-of-year] [_ _ expr] (hsql/call :quarter expr))
-(defmethod sql.qp/date [:presto :year] [_ _ expr] (hsql/call :date_trunc (hx/literal :year) expr))
-
-(defmethod sql.qp/unix-timestamp->honeysql [:presto :seconds]
- [_ _ expr]
- (hsql/call :from_unixtime expr))
-
-(defmethod driver.common/current-db-time-date-formatters :presto
- [_]
- (driver.common/create-db-time-formatters "yyyy-MM-dd'T'HH:mm:ss.SSSZ"))
-
-(defmethod driver.common/current-db-time-native-query :presto
- [_]
- "select to_iso8601(current_timestamp)")
-
-(defmethod driver/current-db-time :presto
- [& args]
- (apply driver.common/current-db-time args))
-
-(doseq [[feature supported?] {:set-timezone true
- :basic-aggregations true
- :standard-deviation-aggregations true
- :expressions true
- :native-parameters true
- :expression-aggregations true
- :binning true
- :foreign-keys true}]
- (defmethod driver/supports? [:presto feature] [_ _] supported?))
diff --git a/modules/drivers/presto/test/metabase/test/data/presto.clj b/modules/drivers/presto/test/metabase/test/data/presto.clj
index 6596ff2d35aa..42e32f2dd01e 100644
--- a/modules/drivers/presto/test/metabase/test/data/presto.clj
+++ b/modules/drivers/presto/test/metabase/test/data/presto.clj
@@ -7,9 +7,11 @@
[metabase.config :as config]
[metabase.driver :as driver]
[metabase.driver.presto :as presto]
+ [metabase.driver.presto-common :as presto-common]
[metabase.driver.sql.util :as sql.u]
[metabase.driver.sql.util.unprepare :as unprepare]
[metabase.test.data.interface :as tx]
+ [metabase.test.data.presto-common]
[metabase.test.data.sql :as sql.tx]))
(sql.tx/add-test-extensions! :presto)
@@ -42,19 +44,20 @@
;; we need a dummy value for every base-type to make a properly typed SELECT statement
(if (keyword? field-type)
(case field-type
- :type/Boolean "TRUE"
- :type/Integer "1"
- :type/BigInteger "cast(1 AS bigint)"
- :type/Float "1.0"
- :type/Decimal "DECIMAL '1.0'"
- :type/Text "cast('' AS VARCHAR)"
- :type/Date "current_timestamp" ; this should probably be a date type, but the test data begs to differ
- :type/DateTime "current_timestamp"
- :type/DateTimeWithTZ "current_timestamp"
- :type/Time "cast(current_time as TIME)"
+ :type/Boolean "TRUE"
+ :type/Integer "1"
+ :type/BigInteger "cast(1 AS bigint)"
+ :type/Float "1.0"
+ :type/Decimal "DECIMAL '1.0'"
+ :type/Text "cast('' AS VARCHAR)"
+ :type/Date "current_timestamp" ; this should be a date type, but the test data begs to differ
+ :type/DateTime "current_timestamp"
+ :type/DateTimeWithTZ "current_timestamp"
+ :type/DateTimeWithZoneOffset "current_timestamp" ; needed for office-checkins
+ :type/Time "cast(current_time as TIME)"
"from_hex('00')") ; this might not be the best default ever
;; we were given a native type, map it back to a base-type and try again
- (field-base-type->dummy-value (#'presto/presto-type->base-type field-type))))
+ (field-base-type->dummy-value (presto-common/presto-type->base-type field-type))))
(defmethod sql.tx/create-table-sql :presto
[driver {:keys [database-name]} {:keys [table-name], :as tabledef}]
@@ -129,15 +132,5 @@
[_ s]
(str/lower-case s))
-(defmethod tx/aggregate-column-info :presto
- ([driver ag-type]
- ((get-method tx/aggregate-column-info ::tx/test-extensions) driver ag-type))
-
- ([driver ag-type field]
- (merge
- ((get-method tx/aggregate-column-info ::tx/test-extensions) driver ag-type field)
- (when (= ag-type :sum)
- {:base_type :type/BigInteger}))))
-
;; FIXME Presto actually has very good timezone support
(defmethod tx/has-questionable-timezone-support? :presto [_] true)
diff --git a/src/metabase/db/spec.clj b/src/metabase/db/spec.clj
index 5fe60ecbc023..a7e9377a93e4 100644
--- a/src/metabase/db/spec.clj
+++ b/src/metabase/db/spec.clj
@@ -2,7 +2,8 @@
"Functions for creating JDBC DB specs for a given driver.
Only databases that are supported as application DBs should have functions in this namespace;
otherwise, similar functions are only needed by drivers, and belong in those namespaces."
- (:require [metabase.config :as config]))
+ (:require [clojure.string :as str]
+ [metabase.config :as config]))
(defn h2
"Create a Clojure JDBC database specification for a H2 database."
@@ -14,8 +15,12 @@
:subname db}
(dissoc opts :db)))
-(defn- make-subname [host port db]
- (str "//" host ":" port "/" db))
+(defn make-subname
+ "Make a subname for the given `host`, `port`, and `db` params. Iff `db` is not blank, then a slash will
+ precede it in the subname."
+ {:arglists '([host port db]), :added "0.39.0"}
+ [host port db]
+ (str "//" host ":" port (if-not (str/blank? db) (str "/" db) "/")))
(defn postgres
"Create a Clojure JDBC database specification for a Postgres database."
diff --git a/src/metabase/driver/sql/query_processor.clj b/src/metabase/driver/sql/query_processor.clj
index ef867bc2c7a9..15bc2f6ca41d 100644
--- a/src/metabase/driver/sql/query_processor.clj
+++ b/src/metabase/driver/sql/query_processor.clj
@@ -100,7 +100,8 @@
:hierarchy #'driver/hierarchy)
(defmulti current-datetime-honeysql-form
- "HoneySQL form that should be used to get the current `datetime` (or equivalent). Defaults to `:%now`."
+ "HoneySQL form that should be used to get the current `datetime` (or equivalent). Defaults to `:%now`. Should ideally
+ include the database type info on the form (ex: via `hx/with-type-info`)."
{:arglists '([driver])}
driver/dispatch-on-initialized-driver
:hierarchy #'driver/hierarchy)
diff --git a/src/metabase/plugins/dependencies.clj b/src/metabase/plugins/dependencies.clj
index 05c930e9ff5c..42ba20da936b 100644
--- a/src/metabase/plugins/dependencies.clj
+++ b/src/metabase/plugins/dependencies.clj
@@ -1,5 +1,7 @@
(ns metabase.plugins.dependencies
- (:require [clojure.tools.logging :as log]
+ (:require [clojure.string :as str]
+ [clojure.tools.logging :as log]
+ [environ.core :as env]
[metabase.plugins.classloader :as classloader]
[metabase.util :as u]
[metabase.util.i18n :refer [trs]]))
@@ -7,10 +9,11 @@
(def ^:private plugins-with-unsatisfied-deps
(atom #{}))
-(defn- dependency-type [{classname :class, plugin :plugin}]
+(defn- dependency-type [{classname :class, plugin :plugin, env-var :env-var}]
(cond
classname :class
plugin :plugin
+ env-var :env-var
:else :unknown))
(defmulti ^:private dependency-satisfied?
@@ -59,6 +62,16 @@
(log-once plugin-name (trs "Plugin ''{0}'' depends on plugin ''{1}''" plugin-name dep-plugin-name))
((set initialized-plugin-names) dep-plugin-name))
+(defmethod dependency-satisfied? :env-var
+ [_ {{plugin-name :name} :info, :as info} {env-var-name :env-var}]
+ (if (str/blank? (env/env (keyword env-var-name)))
+ (do
+ (log-once plugin-name (trs "Plugin ''{0}'' depends on environment variable ''{1}'' being set to something"
+ plugin-name
+ env-var-name))
+ false)
+ true))
+
(defn- all-dependencies-satisfied?*
[initialized-plugin-names {:keys [dependencies], {plugin-name :name} :info, :as info}]
(let [dep-satisfied? (fn [dep]
diff --git a/src/metabase/util/date_2.clj b/src/metabase/util/date_2.clj
index bb51354f1289..0e5304f8fb58 100644
--- a/src/metabase/util/date_2.clj
+++ b/src/metabase/util/date_2.clj
@@ -467,23 +467,15 @@
converts it to the corresponding offset/zoned type; for offset/zoned types, this applies an appropriate timezone
shift."))
-;; We don't know what zone offset to shift this to, since the offset for a zone-id can vary depending on the date
-;; part of a temporal value (e.g. DST vs non-DST). So just adjust to the non-DST "standard" offset for the zone in
-;; question.
-(defn- standard-offset
- "Standard (non-DST) offset for a time zone, for cases when we don't have date information."
- ^java.time.ZoneOffset [^java.time.ZoneId zone-id]
- (.. zone-id getRules (getStandardOffset (t/instant 0))))
-
(extend-protocol WithTimeZoneSameInstant
;; convert to a OffsetTime with no offset (UTC); the OffsetTime method impl will apply the zone shift.
LocalTime
(with-time-zone-same-instant [t zone-id]
- (t/offset-time t (standard-offset zone-id)))
+ (t/offset-time t (common/standard-offset zone-id)))
OffsetTime
(with-time-zone-same-instant [t ^java.time.ZoneId zone-id]
- (t/with-offset-same-instant t (standard-offset zone-id)))
+ (t/with-offset-same-instant t (common/standard-offset zone-id)))
LocalDate
(with-time-zone-same-instant [t zone-id]
diff --git a/src/metabase/util/date_2/common.clj b/src/metabase/util/date_2/common.clj
index 17095312a1f6..8836ee9e85c1 100644
--- a/src/metabase/util/date_2/common.clj
+++ b/src/metabase/util/date_2/common.clj
@@ -1,7 +1,9 @@
(ns metabase.util.date-2.common
(:require [clojure.string :as str]
+ [java-time :as t]
[metabase.util :as u])
- (:import [java.time.temporal ChronoField IsoFields TemporalField WeekFields]))
+ (:import [java.time ZoneId ZoneOffset]
+ [java.time.temporal ChronoField IsoFields TemporalField WeekFields]))
;; TODO - not sure this belongs here, it seems to be a bit more general than just `date-2`.
@@ -33,3 +35,12 @@
:week-fields/week-of-month (.weekOfMonth WeekFields/SUNDAY_START)
:week-fields/week-of-week-based-year (.weekOfWeekBasedYear WeekFields/SUNDAY_START)
:week-fields/week-of-year (.weekOfYear WeekFields/SUNDAY_START)}))
+
+;; We don't know what zone offset to shift this to, since the offset for a zone-id can vary depending on the date
+;; part of a temporal value (e.g. DST vs non-DST). So just adjust to the non-DST "standard" offset for the zone in
+;; question.
+(defn standard-offset
+ "Standard (non-DST) offset for a time zone, for cases when we don't have date information. Gets the offset for the
+ given `zone-id` at January 1 of the current year (since that is the best we can do in this situation)."
+ ^ZoneOffset [^ZoneId zone-id]
+ (.. zone-id getRules (getStandardOffset (t/instant (t/offset-date-time (-> (t/zoned-date-time) t/year t/value) 1 1)))))
diff --git a/src/metabase/util/date_2/parse.clj b/src/metabase/util/date_2/parse.clj
index 518312d1d814..65e85c94746a 100644
--- a/src/metabase/util/date_2/parse.clj
+++ b/src/metabase/util/date_2/parse.clj
@@ -67,7 +67,7 @@
[:zone :date] (ZonedDateTime/of local-date (t/local-time 0) zone-id)
[:offset :date] (OffsetDateTime/of local-date (t/local-time 0) zone-offset)
[:local :date] local-date
- [:zone :time] (OffsetTime/of local-time zone-offset)
+ [:zone :time] (OffsetTime/of local-time (or zone-offset (common/standard-offset zone-id)))
[:offset :time] (OffsetTime/of local-time zone-offset)
[:local :time] local-time
(throw (ex-info (tru "Don''t know how to parse {0} using format {1}" (pr-str s) (pr-str formattr))
diff --git a/src/metabase/util/honeysql_extensions.clj b/src/metabase/util/honeysql_extensions.clj
index 6b04ce1361ed..aa940041c52b 100644
--- a/src/metabase/util/honeysql_extensions.clj
+++ b/src/metabase/util/honeysql_extensions.clj
@@ -204,12 +204,18 @@
(unwrap-typed-honeysql-form [this]
(:form this)))
+(defn type-info->db-type
+ "For a given type-info, returns the `database-type`."
+ [type-info]
+ {:added "0.39.0"}
+ (::database-type type-info))
+
(defn is-of-type?
"Is `honeysql-form` a typed form with `database-type`?
(is-of-type? expr \"datetime\") ; -> true"
[honeysql-form database-type]
- (= (::database-type (type-info honeysql-form))
+ (= (type-info->db-type (type-info honeysql-form))
(some-> database-type name str/lower-case)))
(s/defn with-database-type-info
diff --git a/test/metabase/query_processor_test/breakout_test.clj b/test/metabase/query_processor_test/breakout_test.clj
index 2347486dac29..0eeb1f52f23d 100644
--- a/test/metabase/query_processor_test/breakout_test.clj
+++ b/test/metabase/query_processor_test/breakout_test.clj
@@ -244,7 +244,8 @@
{:source-query
{:source-table $$venues
:aggregation [[:count]]
- :breakout [[:field %latitude {:binning {:strategy :default}}]]}}))))))
+ :breakout [[:field %latitude {:binning {:strategy :default}}]]}
+ :order-by [[:asc $latitude]]}))))))
(testing "Binning is not supported when there is no fingerprint to determine boundaries"
;; Unfortunately our new `add-source-metadata` middleware is just too good at what it does and will pull in
diff --git a/test/metabase/query_processor_test/date_bucketing_test.clj b/test/metabase/query_processor_test/date_bucketing_test.clj
index 36132b145a44..412a20ed4019 100644
--- a/test/metabase/query_processor_test/date_bucketing_test.clj
+++ b/test/metabase/query_processor_test/date_bucketing_test.clj
@@ -16,11 +16,13 @@
their results."
(:require [clj-time.core :as time]
[clj-time.format :as tformat]
+ [clojure.string :as str]
[clojure.test :refer :all]
[java-time :as t]
[metabase.driver :as driver]
[metabase.driver.sql.query-processor :as sql.qp]
[metabase.driver.sql.query-processor-test :as sql.qp.test]
+ [metabase.driver.sql-jdbc.sync :as sql-jdbc.sync]
[metabase.models.database :refer [Database]]
[metabase.query-processor :as qp]
[metabase.query-processor-test :as qp.test]
@@ -28,6 +30,7 @@
[metabase.test :as mt]
[metabase.util :as u]
[metabase.util.date-2 :as u.date]
+ [metabase.util.honeysql-extensions :as hx]
[potemkin.types :as p.types]
[pretty.core :as pretty]
[toucan.db :as db])
@@ -848,13 +851,24 @@
(pretty [_]
(list 'TimestampDatasetDef. intervalSeconds)))
+(defn- driver->current-datetime-base-type
+ "Returns the :base-type of the \"current timestamp\" HoneySQL form defined by the driver `d`. Relies upon the driver
+ implementation having set that explicitly via `hx/with-type-info`. Returns `nil` if it can't be determined."
+ [d]
+ (when (isa? driver/hierarchy driver/*driver* :sql)
+ (let [db-type (-> (sql.qp/current-datetime-honeysql-form d)
+ hx/type-info
+ hx/type-info->db-type)]
+ (when-not (str/blank? db-type)
+ (sql-jdbc.sync/database-type->base-type d db-type)))))
+
(defmethod mt/get-dataset-definition TimestampDatasetDef
[^TimestampDatasetDef this]
(let [interval-seconds (.intervalSeconds this)]
(mt/dataset-definition (str "checkins_interval_" interval-seconds)
["checkins"
[{:field-name "timestamp"
- :base-type :type/DateTime}]
+ :base-type (or (driver->current-datetime-base-type driver/*driver*) :type/DateTime)}]
(vec (for [i (range -15 15)]
;; TIMESTAMP FIXME — not sure if still needed
;;
@@ -1077,9 +1091,15 @@
(testing "Additional tests for filtering against various datetime bucketing units that aren't tested above"
(mt/test-drivers (mt/normal-drivers)
(doseq [[expected-count unit filter-value] addition-unit-filtering-vals]
- (testing (format "\nunit = %s" unit)
- (is (= expected-count (count-of-checkins unit filter-value))
- (format "count of rows where (= (%s date) %s) should be %d" (name unit) filter-value expected-count)))))))
+ (doseq [tz [nil "UTC"]] ;iterate on at least two report time zones to suss out bugs related to that
+ (mt/with-temporary-setting-values [report-timezone tz]
+ (testing (format "\nunit = %s" unit)
+ (is (= expected-count (count-of-checkins unit filter-value))
+ (format
+ "count of rows where (= (%s date) %s) should be %d"
+ (name unit)
+ filter-value
+ expected-count)))))))))
(deftest legacy-default-datetime-bucketing-test
(testing (str ":type/Date or :type/DateTime fields that don't have `:temporal-unit` clauses should get default `:day` "
diff --git a/test/metabase/query_processor_test/expressions_test.clj b/test/metabase/query_processor_test/expressions_test.clj
index cff90843b1d1..3957181e3e8e 100644
--- a/test/metabase/query_processor_test/expressions_test.clj
+++ b/test/metabase/query_processor_test/expressions_test.clj
@@ -353,11 +353,11 @@
{:source-query {:source-table (mt/id :venues)
:aggregation [[:min (mt/id :venues :price)]
[:max (mt/id :venues :price)]]
- :breakout [[:field (mt/id :venues :name) nil]]}
+ :breakout [[:field (mt/id :venues :name) nil]]
+ :limit 3}
:expressions {:price-range [:-
[:field "max" {:base-type :type/Number}]
- [:field "min" {:base-type :type/Number}]]}
- :limit 3})))))))
+ [:field "min" {:base-type :type/Number}]]}})))))))
(deftest fk-field-and-duplicate-names-test
;; Redshift hangs on sample-dataset -- See #14784
diff --git a/test/metabase/query_processor_test/filter_test.clj b/test/metabase/query_processor_test/filter_test.clj
index 96fc24acda66..067aa12d5dde 100644
--- a/test/metabase/query_processor_test/filter_test.clj
+++ b/test/metabase/query_processor_test/filter_test.clj
@@ -445,8 +445,8 @@
(mt/test-drivers (mt/normal-drivers)
(testing "The QP should automatically parse String parameters in filter clauses to the correct type"
(testing "String parameter to an Integer Field"
- (is (= (mt/rows (mt/run-mbql-query venues {:filter [:= $price 4]}))
- (mt/rows (mt/run-mbql-query venues {:filter [:= $price "4"]}))))))))
+ (is (= (mt/rows (mt/run-mbql-query venues {:filter [:= $price 4] :order-by [[:asc $id]]}))
+ (mt/rows (mt/run-mbql-query venues {:filter [:= $price "4"] :order-by [[:asc $id]]}))))))))
;; For the tests below:
;;
diff --git a/test/metabase/query_processor_test/parameters_test.clj b/test/metabase/query_processor_test/parameters_test.clj
index ada1b027bee7..f1de41d12e93 100644
--- a/test/metabase/query_processor_test/parameters_test.clj
+++ b/test/metabase/query_processor_test/parameters_test.clj
@@ -86,6 +86,22 @@
:target [:dimension [:template-tag (name field)]]
:value value}]})
+;; TODO: fix this test for Presto JDBC (detailed explanation follows)
+;; Spent a few hours and need to move on. Here is the query being generated for the failing case
+;; SELECT count(*) AS "count" FROM "default"."attempts"
+;; WHERE date_trunc('day', "default"."attempts"."datetime_tz") = date '2019-11-12';
+;; And here is what it *SHOULD* be to pass the test
+;; SELECT count(*) AS "count" FROM "default"."attempts"
+;; WHERE date_trunc('day', "default"."attempts"."datetime_tz" AT TIME ZONE 'UTC') = date '2019-11-12';
+;; Notice the AT TIME ZONE 'UTC' part. In this case, the test does not set a report timezone, so a fallback of UTC
+;; should (evidently) be applied.
+;; We need the type information, that the datetime_tz is `timestamp with time zone`, to be available to
+;; (defmethod sql.qp/date [:presto-jdbc :day]
+;; However, it is not available there. The expression's HSQL type-info and db-type are both nil. Somehow need to tell
+;; the query processor (or something else?) to *include* that type information when running this test, because it's
+;; clearly known (i.e. if you sync the DB and then query the `metabase_field`, it is there and is correct.
+;; Tried manually syncing the DB (with attempted-murders dataset), and storing it to an initialized QP, to no avail.
+
;; this isn't a complete test for all possible field filter types, but it covers mostly everything
(deftest field-filter-param-test
(letfn [(is-count-= [expected-count table field value-type value]
diff --git a/test/metabase/query_processor_test/timezones_test.clj b/test/metabase/query_processor_test/timezones_test.clj
index 17857873d4e0..0eb62eed0808 100644
--- a/test/metabase/query_processor_test/timezones_test.clj
+++ b/test/metabase/query_processor_test/timezones_test.clj
@@ -119,8 +119,8 @@
(hsql/raw "{{date1}}")
(hsql/raw "{{date2}}")]
:order-by [[(field-identifier :users :id) :asc]]})
- :template-tags {:date1 {:name "date1" :display_name "Date1" :type "date" }
- :date2 {:name "date2" :display_name "Date2" :type "date" }}}
+ :template-tags {:date1 {:name "date1" :display_name "Date1" :type "date"}
+ :date2 {:name "date2" :display_name "Date2" :type "date"}}}
:parameters [{:type "date/single"
:target ["variable" ["template-tag" "date1"]]
:value "2014-08-02T02:00:00.000000"}
@@ -224,6 +224,6 @@
(mt/dataset attempted-murders
(doseq [timezone [nil "US/Pacific" "US/Eastern" "Asia/Hong_Kong"]]
(mt/with-temporary-setting-values [report-timezone timezone]
- (let [expected (expected-attempts)]
- (is (= expected
- (select-keys (attempts) (keys expected))))))))))
+ (let [expected (expected-attempts)
+ actual (select-keys (attempts) (keys expected))]
+ (is (= expected actual))))))))
diff --git a/test/metabase/test/data/dataset_definitions.clj b/test/metabase/test/data/dataset_definitions.clj
index 6eb9851b68ee..df74a3751cc5 100644
--- a/test/metabase/test/data/dataset_definitions.clj
+++ b/test/metabase/test/data/dataset_definitions.clj
@@ -209,5 +209,4 @@
(t/local-time t) ; time
(t/offset-time t) ; time-ltz
(t/offset-time t) ; time-tz
- cnt ; num-crows
- ])]])
+ cnt])]]) ; num-crows
diff --git a/test/metabase/test/data/dataset_definitions/office-checkins.edn b/test/metabase/test/data/dataset_definitions/office-checkins.edn
index 92c42085a468..55ff4a78384e 100644
--- a/test/metabase/test/data/dataset_definitions/office-checkins.edn
+++ b/test/metabase/test/data/dataset_definitions/office-checkins.edn
@@ -1,5 +1,5 @@
[["checkins" [{:field-name "person", :base-type :type/Text}
- {:field-name "timestamp", :base-type :type/DateTime}]
+ {:field-name "timestamp", :base-type :type/DateTimeWithZoneOffset}]
[["Cam", #t "2019-01-02T05:30:00.000-07:00"]
["Cam", #t "2019-01-09T05:30:00.000-07:00"]
["Kyle", #t "2019-01-06T08:30:00.000-07:00"]
diff --git a/test/metabase/util/date_2_test.clj b/test/metabase/util/date_2_test.clj
index d84aa4dc9326..69696a8f4b48 100644
--- a/test/metabase/util/date_2_test.clj
+++ b/test/metabase/util/date_2_test.clj
@@ -586,3 +586,11 @@
(testing "can handle infinity dates (#12761)"
(is (u.date/with-time-zone-same-instant java.time.OffsetDateTime/MAX (t/zone-id "UTC")))
(is (u.date/with-time-zone-same-instant java.time.OffsetDateTime/MIN (t/zone-id "UTC")))))
+
+(deftest standard-offset-test
+ (testing "standard-offset works correctly, regardless of system clock timezone"
+ (doseq [clk [#t "2021-01-01T00:00-06:00[US/Central]" ; one in CST
+ #t "2021-07-01T00:00-05:00[US/Central]"]] ; one in CDT
+ (mt/with-clock clk
+ (is (= (t/zone-offset "-06:00") (u.date.common/standard-offset (t/zone-id "America/Chicago"))))
+ (is (= (t/zone-offset "Z") (u.date.common/standard-offset (t/zone-id "UTC"))))))))
From bc90e06915741686dee4e074be6d7f13cd4aeba0 Mon Sep 17 00:00:00 2001
From: Dalton
Date: Fri, 23 Jul 2021 15:05:04 -0700
Subject: [PATCH 096/664] add question details sidebar (#16803)
* add question details sidebar (code from bucm)
update some cy tests
rmv moderation-related code
rmv padding on description
merge history button logic into label
lint fix
update some clicks in cy tests
update styles to use styled-components
fix more cy tests
rmv unused component
rmv unused reference
rmv unused prop
remove styling code that is no longer necessary
rmv extraneous space
fix translation
rmv added util class
move styled component to own file
* add revision util tests
* rmv some unneeded question panel styling
* move files into query_builder directory
* rearrange stuff in file
* ClampedDescription unit test
* add QuestionActionButtons unit tests
* add SavedQuestionHeaderButton test
* add QuestionActivityTimeline tests
* tweak/improve props/propTypes
* fix cy test referencing question description
* fix revision history cy test
* Question sidebar visual polish (#16942)
* style tweaks
* restyle Revert button
* add a little breadcrumb spacing
* make LastEditInfoLabel use Badge instead of TextButton
* bring back TextButton
* Revision History => History
* fix activity timeline unit test
* rmv unused styled components
Co-authored-by: Dalton Johnson
* hide 'add a description' button when read only
* update permissions cy tests
* update ClampedDescription unit test
* use size enum instead of boolean
* move export to bottom of file
* move modal magic strings to a constants file
* clobber all qb state when reloading
* Fix reversion cy tests
Co-authored-by: Maz Ameli
---
.../src/metabase/components/ActionButton.jsx | 14 ++-
frontend/src/metabase/components/Badge.jsx | 10 +-
.../src/metabase/components/Button.styled.jsx | 27 +++++
.../src/metabase/components/ClampedText.jsx | 43 +++----
.../components/ClampedText.styled.jsx | 12 ++
.../src/metabase/components/HistoryModal.jsx | 13 +--
.../metabase/components/LastEditInfoLabel.js | 24 ++--
frontend/src/metabase/components/Timeline.jsx | 96 ++++++++--------
.../metabase/components/Timeline.styled.jsx | 53 +++++++++
frontend/src/metabase/lib/revisions.js | 47 ++++++++
frontend/src/metabase/lib/time.js | 4 +
.../src/metabase/query_builder/actions.js | 36 +++++-
.../components/ClampedDescription.jsx | 28 +++++
.../query_builder/components/QueryModals.jsx | 44 +++----
.../components/QuestionActionButtons.jsx | 84 ++++++++++++++
.../QuestionActionButtons.styled.jsx | 8 ++
.../components/QuestionActivityTimeline.jsx | 87 ++++++++++++++
.../QuestionActivityTimeline.styled.jsx | 37 ++++++
.../components/SavedQuestionHeaderButton.jsx | 28 +++++
.../SavedQuestionHeaderButton.styled.jsx | 16 +++
.../components/view/QuestionEntityMenu.jsx | 53 ---------
.../components/view/QuestionFilters.jsx | 2 +-
.../query_builder/components/view/View.jsx | 5 +
.../components/view/ViewHeader.jsx | 45 ++++----
.../components/view/ViewHeader.styled.jsx | 6 +
.../components/view/ViewSidebar.jsx | 6 +-
.../view/sidebars/QuestionDetailsSidebar.jsx | 28 +++++
.../sidebars/QuestionDetailsSidebarPanel.jsx | 37 ++++++
.../QuestionDetailsSidebarPanel.styled.jsx | 8 ++
.../components/view/sidebars/constants.js | 3 +
.../src/metabase/query_builder/constants.js | 15 +++
.../query_builder/containers/QueryBuilder.jsx | 5 +-
.../src/metabase/query_builder/reducers.js | 18 ++-
.../components/ClampedText.unit.spec.js | 61 ++++++++++
.../test/metabase/lib/revisions.unit.spec.js | 107 ++++++++++++++++++
.../ClampedDescription.unit.spec.js | 37 ++++++
.../QuestionActionButtons.unit.spec.js | 75 ++++++++++++
.../QuestionActivityTimeline.unit.spec.jsx | 79 +++++++++++++
.../SavedQuestionHeaderButton.unit.spec.js | 34 ++++++
.../collections/items-metadata.cy.spec.js | 4 +-
.../collections/permissions.cy.spec.js | 98 +++++++++++-----
.../reproductions/12581.cy.spec.js | 3 +-
.../scenarios/question/saved.cy.spec.js | 4 +-
.../scenarios/sharing/public.cy.spec.js | 6 +-
.../smoketest/admin_setup.cy.spec.js | 13 ++-
45 files changed, 1209 insertions(+), 254 deletions(-)
create mode 100644 frontend/src/metabase/components/Button.styled.jsx
create mode 100644 frontend/src/metabase/components/ClampedText.styled.jsx
create mode 100644 frontend/src/metabase/components/Timeline.styled.jsx
create mode 100644 frontend/src/metabase/lib/revisions.js
create mode 100644 frontend/src/metabase/query_builder/components/ClampedDescription.jsx
create mode 100644 frontend/src/metabase/query_builder/components/QuestionActionButtons.jsx
create mode 100644 frontend/src/metabase/query_builder/components/QuestionActionButtons.styled.jsx
create mode 100644 frontend/src/metabase/query_builder/components/QuestionActivityTimeline.jsx
create mode 100644 frontend/src/metabase/query_builder/components/QuestionActivityTimeline.styled.jsx
create mode 100644 frontend/src/metabase/query_builder/components/SavedQuestionHeaderButton.jsx
create mode 100644 frontend/src/metabase/query_builder/components/SavedQuestionHeaderButton.styled.jsx
delete mode 100644 frontend/src/metabase/query_builder/components/view/QuestionEntityMenu.jsx
create mode 100644 frontend/src/metabase/query_builder/components/view/ViewHeader.styled.jsx
create mode 100644 frontend/src/metabase/query_builder/components/view/sidebars/QuestionDetailsSidebar.jsx
create mode 100644 frontend/src/metabase/query_builder/components/view/sidebars/QuestionDetailsSidebarPanel.jsx
create mode 100644 frontend/src/metabase/query_builder/components/view/sidebars/QuestionDetailsSidebarPanel.styled.jsx
create mode 100644 frontend/src/metabase/query_builder/components/view/sidebars/constants.js
create mode 100644 frontend/src/metabase/query_builder/constants.js
create mode 100644 frontend/test/metabase/components/ClampedText.unit.spec.js
create mode 100644 frontend/test/metabase/lib/revisions.unit.spec.js
create mode 100644 frontend/test/metabase/query_builder/components/ClampedDescription.unit.spec.js
create mode 100644 frontend/test/metabase/query_builder/components/QuestionActionButtons.unit.spec.js
create mode 100644 frontend/test/metabase/query_builder/components/QuestionActivityTimeline.unit.spec.jsx
create mode 100644 frontend/test/metabase/query_builder/components/SavedQuestionHeaderButton.unit.spec.js
diff --git a/frontend/src/metabase/components/ActionButton.jsx b/frontend/src/metabase/components/ActionButton.jsx
index 758c549e2379..9b0d73761352 100644
--- a/frontend/src/metabase/components/ActionButton.jsx
+++ b/frontend/src/metabase/components/ActionButton.jsx
@@ -11,6 +11,8 @@ import cx from "classnames";
type Props = {
actionFn: (...args: any[]) => Promise,
className?: string,
+ successClassName?: string,
+ failedClassName?: string,
children?: any,
normalText?: string,
activeText?: string,
@@ -46,6 +48,8 @@ export default class ActionButton extends Component {
static defaultProps = {
className: "Button",
+ successClassName: "Button--success",
+ failedClassName: "Button--danger",
normalText: t`Save`,
activeText: t`Saving...`,
failedText: t`Save failed`,
@@ -118,11 +122,14 @@ export default class ActionButton extends Component {
// eslint-disable-next-line no-unused-vars
actionFn,
className,
+ successClassName,
+ failedClassName,
forceActiveStyle,
children,
...props
} = this.props;
const { active, result } = this.state;
+ const isActionDisabled = active || result === "success";
return (
|
- {this.getRevisionDescription(revision)}
+ {getRevisionDescription(revision)}
|
{shouldRenderRevertButton && (
diff --git a/frontend/src/metabase/components/LastEditInfoLabel.js b/frontend/src/metabase/components/LastEditInfoLabel.js
index 2fddf0a1fcb2..d5e42b7e886d 100644
--- a/frontend/src/metabase/components/LastEditInfoLabel.js
+++ b/frontend/src/metabase/components/LastEditInfoLabel.js
@@ -1,18 +1,12 @@
import React from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
-import styled from "styled-components";
import { t } from "ttag";
import moment from "moment";
-import { color } from "metabase/lib/colors";
-
import { getUser } from "metabase/selectors/user";
-const Label = styled.span`
- font-weight: bold;
- color: ${color("text-medium")};
-`;
+import { TextButton } from "metabase/components/Button.styled";
function mapStateToProps(state) {
return {
@@ -33,6 +27,7 @@ LastEditInfoLabel.propTypes = {
user: PropTypes.shape({
id: PropTypes.number,
}).isRequired,
+ onClick: PropTypes.func.isRequired,
};
function formatEditorName(firstName, lastName) {
@@ -40,16 +35,23 @@ function formatEditorName(firstName, lastName) {
return `${firstName} ${lastNameFirstLetter}.`;
}
-function LastEditInfoLabel({ item, user, ...props }) {
+function LastEditInfoLabel({ item, user, onClick, ...props }) {
const { first_name, last_name, id: editorId, timestamp } = item[
"last-edit-info"
];
const time = moment(timestamp).fromNow();
const editor =
- editorId === user.id ? "you" : formatEditorName(first_name, last_name);
-
- return ;
+ editorId === user.id ? t`you` : formatEditorName(first_name, last_name);
+
+ return (
+ {t`Edited ${time} by ${editor}`}
+ );
}
export default connect(mapStateToProps)(LastEditInfoLabel);
diff --git a/frontend/src/metabase/components/Timeline.jsx b/frontend/src/metabase/components/Timeline.jsx
index 22b7c19cf25b..4867029257f9 100644
--- a/frontend/src/metabase/components/Timeline.jsx
+++ b/frontend/src/metabase/components/Timeline.jsx
@@ -1,36 +1,37 @@
import React, { useMemo } from "react";
import PropTypes from "prop-types";
import _ from "underscore";
-import styled from "styled-components";
-import moment from "moment";
+import { getRelativeTime } from "metabase/lib/time";
-import { color } from "metabase/lib/colors";
+import {
+ TimelineContainer,
+ TimelineItem,
+ Border,
+ ItemIcon,
+ ItemBody,
+ ItemHeader,
+ Timestamp,
+ ItemFooter,
+} from "./Timeline.styled";
-import Icon from "metabase/components/Icon";
-
-const TimelineContainer = styled.div`
- position: relative;
- margin-left: ${props => props.leftShift}px;
- margin-bottom: ${props => props.bottomShift}px;
-`;
-
-const TimelineItem = styled.div`
- transform: translateX(-${props => props.leftShift}px);
- white-space: pre-line;
-`;
+Timeline.propTypes = {
+ className: PropTypes.string,
+ items: PropTypes.arrayOf(
+ PropTypes.shape({
+ timestamp: PropTypes.number.isRequired,
+ icon: PropTypes.string.isRequired,
+ title: PropTypes.string.isRequired,
+ description: PropTypes.string,
+ renderFooter: PropTypes.bool,
+ }),
+ ),
+ renderFooter: PropTypes.func,
+};
-// shift the border down slightly so that it doesn't appear above the top-most icon
-const Border = styled.div`
- position: absolute;
- top: ${props => props.borderShift}px;
- left: 0;
- right: 0;
- bottom: -${props => props.borderShift}px;
- border-left: 1px solid ${color("border")};
-`;
+export default Timeline;
-const Timeline = ({ className, items = [], renderFooter }) => {
- const iconSize = 20;
+function Timeline({ className, items = [], renderFooter }) {
+ const iconSize = 16;
const halfIconSize = iconSize / 2;
const sortedFormattedItems = useMemo(() => {
@@ -39,7 +40,7 @@ const Timeline = ({ className, items = [], renderFooter }) => {
.map(item => {
return {
...item,
- formattedTimestamp: moment(item.timestamp).fromNow(),
+ formattedTimestamp: getRelativeTime(item.timestamp),
};
});
}, [items]);
@@ -50,35 +51,32 @@ const Timeline = ({ className, items = [], renderFooter }) => {
bottomShift={halfIconSize}
className={className}
>
-
{sortedFormattedItems.map((item, index) => {
- const { icon, title, description, formattedTimestamp } = item;
+ const {
+ icon,
+ title,
+ description,
+ timestamp,
+ formattedTimestamp,
+ } = item;
const key = item.key == null ? index : item.key;
+ const isNotLastEvent = index !== sortedFormattedItems.length - 1;
return (
-
-
-
- {title}
- {formattedTimestamp}
+
+ {isNotLastEvent && }
+
+
+ {title}
+ {formattedTimestamp}
{description}
- {_.isFunction(renderFooter) && {renderFooter(item)} }
-
+ {_.isFunction(renderFooter) && (
+ {renderFooter(item)}
+ )}
+
);
})}
);
-};
-
-Timeline.propTypes = {
- className: PropTypes.string,
- items: PropTypes.array,
- renderFooter: PropTypes.func,
-};
-
-export default Timeline;
+}
diff --git a/frontend/src/metabase/components/Timeline.styled.jsx b/frontend/src/metabase/components/Timeline.styled.jsx
new file mode 100644
index 000000000000..1c04dc66b883
--- /dev/null
+++ b/frontend/src/metabase/components/Timeline.styled.jsx
@@ -0,0 +1,53 @@
+import styled from "styled-components";
+import { color } from "metabase/lib/colors";
+import Icon from "metabase/components/Icon";
+
+export const TimelineContainer = styled.div`
+ position: relative;
+ margin-left: ${props => props.leftShift}px;
+ margin-bottom: ${props => props.bottomShift}px;
+`;
+
+export const TimelineItem = styled.div`
+ display: flex;
+ align-items: start;
+ justify-content: start;
+ transform: translateX(-${props => props.leftShift}px);
+ white-space: pre-line;
+ width: 100%;
+ margin-bottom: 1rem;
+`;
+
+export const ItemIcon = styled(Icon)`
+ position: relative;
+ color: ${color("text-light")};
+`;
+
+export const ItemBody = styled.div`
+ margin-left: 0.5rem;
+ flex: 1;
+`;
+
+export const ItemHeader = styled.div`
+ font-weight: 700;
+`;
+
+export const Timestamp = styled.time`
+ color: ${color("text-medium")}
+ font-size: 0.875em;
+ padding-bottom: 0.5rem;
+`;
+
+export const ItemFooter = styled.div`
+ margin-top: 0.5rem;
+`;
+
+// shift the border down slightly so that it doesn't appear above the top-most icon
+// also using a negative `bottom` to connect the border with the event icon beneath it
+export const Border = styled.div`
+ position: absolute;
+ top: ${props => props.borderShift}px;
+ left: ${props => props.borderShift}px;
+ bottom: calc(-1rem - ${props => props.borderShift}px);
+ border-left: 1px solid ${color("border")};
+`;
diff --git a/frontend/src/metabase/lib/revisions.js b/frontend/src/metabase/lib/revisions.js
new file mode 100644
index 000000000000..393118673d5e
--- /dev/null
+++ b/frontend/src/metabase/lib/revisions.js
@@ -0,0 +1,47 @@
+import { t } from "ttag";
+
+export const REVISION_EVENT_ICON = "pencil";
+
+export function getRevisionDescription(revision) {
+ if (isCreationEvent(revision)) {
+ return "First revision.";
+ } else if (isReversionEvent(revision)) {
+ return t`Reverted to an earlier revision and ${revision.description}`;
+ } else {
+ return revision.description;
+ }
+}
+
+export function getRevisionEventsForTimeline(revisions = [], canWrite) {
+ return revisions.map((revision, index) => {
+ const isCreation = isCreationEvent(revision);
+ const isRevertable = canWrite && index !== 0;
+ const username = getRevisionUsername(revision);
+ return {
+ timestamp: getRevisionEpochTimestamp(revision),
+ icon: REVISION_EVENT_ICON,
+ title: isCreation
+ ? t`${username} created this`
+ : t`${username} edited this`,
+ description: isCreation ? undefined : getRevisionDescription(revision),
+ isRevertable,
+ revision,
+ };
+ });
+}
+
+function isCreationEvent(revision) {
+ return revision.is_creation;
+}
+
+function isReversionEvent(revision) {
+ return revision.is_reversion;
+}
+
+function getRevisionUsername(revision) {
+ return revision.user.common_name;
+}
+
+function getRevisionEpochTimestamp(revision) {
+ return new Date(revision.timestamp).valueOf();
+}
diff --git a/frontend/src/metabase/lib/time.js b/frontend/src/metabase/lib/time.js
index b786fa04dfc9..584980ecc5e2 100644
--- a/frontend/src/metabase/lib/time.js
+++ b/frontend/src/metabase/lib/time.js
@@ -119,3 +119,7 @@ export function formatFrame(frame) {
return frame;
}
}
+
+export function getRelativeTime(timestamp) {
+ return moment(timestamp).fromNow();
+}
diff --git a/frontend/src/metabase/query_builder/actions.js b/frontend/src/metabase/query_builder/actions.js
index d8d88b8182c8..fbb88606f926 100644
--- a/frontend/src/metabase/query_builder/actions.js
+++ b/frontend/src/metabase/query_builder/actions.js
@@ -123,6 +123,13 @@ export const onCloseChartSettings = createAction(
"metabase/qb/CLOSE_CHART_SETTINGS",
);
export const onOpenChartType = createAction("metabase/qb/OPEN_CHART_TYPE");
+export const onOpenQuestionDetails = createAction(
+ "metabase/qb/OPEN_QUESTION_DETAILS",
+);
+export const onCloseQuestionDetails = createAction(
+ "metabase/qb/CLOSE_QUESTION_DETAILS",
+);
+
export const onCloseChartType = createAction("metabase/qb/CLOSE_CHART_TYPE");
export const onCloseSidebars = createAction("metabase/qb/CLOSE_SIDEBARS");
@@ -721,10 +728,26 @@ export const setParameterValue = createAction(
},
);
+// refetches the card without triggering a run of the card's query
+export const SOFT_RELOAD_CARD = "metabase/qb/SOFT_RELOAD_CARD";
+export const softReloadCard = createThunkAction(SOFT_RELOAD_CARD, () => {
+ return async (dispatch, getState) => {
+ const outdatedCard = getCard(getState());
+ const action = await dispatch(
+ Questions.actions.fetch({ id: outdatedCard.id }, { reload: true }),
+ );
+
+ return Questions.HACK_getObjectFromAction(action);
+ };
+});
+
export const RELOAD_CARD = "metabase/qb/RELOAD_CARD";
export const reloadCard = createThunkAction(RELOAD_CARD, () => {
return async (dispatch, getState) => {
- const outdatedCard = getState().qb.card;
+ const outdatedCard = getCard(getState());
+
+ dispatch(resetQB());
+
const action = await dispatch(
Questions.actions.fetch({ id: outdatedCard.id }, { reload: true }),
);
@@ -1333,3 +1356,14 @@ export const showChartSettings = createAction(SHOW_CHART_SETTINGS);
// these are just temporary mappings to appease the existing QB code and it's naming prefs
export const onUpdateVisualizationSettings = updateCardVisualizationSettings;
export const onReplaceAllVisualizationSettings = replaceAllCardVisualizationSettings;
+
+export const REVERT_TO_REVISION = "metabase/qb/REVERT_TO_REVISION";
+export const revertToRevision = createThunkAction(
+ REVERT_TO_REVISION,
+ revision => {
+ return async dispatch => {
+ await revision.revert();
+ await dispatch(reloadCard());
+ };
+ },
+);
diff --git a/frontend/src/metabase/query_builder/components/ClampedDescription.jsx b/frontend/src/metabase/query_builder/components/ClampedDescription.jsx
new file mode 100644
index 000000000000..14f2b4ee440a
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/ClampedDescription.jsx
@@ -0,0 +1,28 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { t } from "ttag";
+
+import ClampedText from "metabase/components/ClampedText";
+import { TextButton } from "metabase/components/Button.styled";
+
+ClampedDescription.propTypes = {
+ className: PropTypes.string,
+ description: PropTypes.string,
+ onEdit: PropTypes.func,
+};
+
+export function ClampedDescription({ className, description, onEdit }) {
+ if (!description && !onEdit) {
+ return null;
+ }
+
+ return (
+
+ {description ? (
+
+ ) : (
+ {t`Add a description`}
+ )}
+
+ );
+}
diff --git a/frontend/src/metabase/query_builder/components/QueryModals.jsx b/frontend/src/metabase/query_builder/components/QueryModals.jsx
index 3ac806057100..c78583863e05 100644
--- a/frontend/src/metabase/query_builder/components/QueryModals.jsx
+++ b/frontend/src/metabase/query_builder/components/QueryModals.jsx
@@ -4,6 +4,8 @@ import React from "react";
import { t } from "ttag";
import _ from "underscore";
+import { MODAL_TYPES } from "metabase/query_builder/constants";
+
import Modal from "metabase/components/Modal";
import SaveQuestionModal from "metabase/containers/SaveQuestionModal";
@@ -34,13 +36,13 @@ export default class QueryModals extends React.Component {
onCloseModal();
} else {
// HACK: in a timeout because save modal closes itself
- setTimeout(() => onOpenModal("create-alert"));
+ setTimeout(() => onOpenModal(MODAL_TYPES.CREATE_ALERT));
}
};
render() {
const { modal, question, onCloseModal, onOpenModal } = this.props;
- return modal === "save" ? (
+ return modal === MODAL_TYPES.SAVE ? (
{
await this.props.onCreate(card);
- onOpenModal("saved");
+ onOpenModal(MODAL_TYPES.SAVED);
}}
onClose={onCloseModal}
/>
- ) : modal === "saved" ? (
+ ) : modal === MODAL_TYPES.SAVED ? (
{
- onOpenModal("add-to-dashboard");
+ onOpenModal(MODAL_TYPES.ADD_TO_DASHBOARD);
}}
/>
- ) : modal === "add-to-dashboard-save" ? (
+ ) : modal === MODAL_TYPES.ADD_TO_DASHBOARD_SAVE ? (
{
await this.props.onSave(card);
- onOpenModal("add-to-dashboard");
+ onOpenModal(MODAL_TYPES.ADD_TO_DASHBOARD);
}}
onCreate={async card => {
await this.props.onCreate(card);
- onOpenModal("add-to-dashboard");
+ onOpenModal(MODAL_TYPES.ADD_TO_DASHBOARD);
}}
onClose={onCloseModal}
multiStep
/>
- ) : modal === "add-to-dashboard" ? (
+ ) : modal === MODAL_TYPES.ADD_TO_DASHBOARD ? (
- ) : modal === "create-alert" ? (
+ ) : modal === MODAL_TYPES.CREATE_ALERT ? (
- ) : modal === "save-question-before-alert" ? (
+ ) : modal === MODAL_TYPES.SAVE_QUESTION_BEFORE_ALERT ? (
- ) : modal === "save-question-before-embed" ? (
+ ) : modal === MODAL_TYPES.SAVE_QUESTION_BEFORE_EMBED ? (
{
await this.props.onSave(card, false);
- onOpenModal("embed");
+ onOpenModal(MODAL_TYPES.EMBED);
}}
onCreate={async card => {
await this.props.onCreate(card, false);
- onOpenModal("embed");
+ onOpenModal(MODAL_TYPES.EMBED);
}}
onClose={onCloseModal}
multiStep
initialCollectionId={this.props.initialCollectionId}
/>
- ) : modal === "history" ? (
+ ) : modal === MODAL_TYPES.HISTORY ? (
- ) : modal === "move" ? (
+ ) : modal === MODAL_TYPES.MOVE ? (
- ) : modal === "archive" ? (
+ ) : modal === MODAL_TYPES.ARCHIVE ? (
- ) : modal === "edit" ? (
+ ) : modal === MODAL_TYPES.EDIT ? (
this.props.onSave(card, false)}
/>
- ) : modal === "embed" ? (
+ ) : modal === MODAL_TYPES.EMBED ? (
- ) : modal === "clone" ? (
+ ) : modal === MODAL_TYPES.CLONE ? (
onOpenModal("saved")}
+ onSaved={() => onOpenModal(MODAL_TYPES.SAVED)}
/>
) : null;
diff --git a/frontend/src/metabase/query_builder/components/QuestionActionButtons.jsx b/frontend/src/metabase/query_builder/components/QuestionActionButtons.jsx
new file mode 100644
index 000000000000..33d7651419b1
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/QuestionActionButtons.jsx
@@ -0,0 +1,84 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { t } from "ttag";
+
+import { MODAL_TYPES } from "metabase/query_builder/constants";
+
+import Button from "metabase/components/Button";
+import Tooltip from "metabase/components/Tooltip";
+import { Container } from "./QuestionActionButtons.styled";
+
+export const EDIT_TESTID = "edit-details-button";
+export const ADD_TO_DASH_TESTID = "add-to-dashboard-button";
+export const MOVE_TESTID = "move-button";
+export const CLONE_TESTID = "clone-button";
+export const ARCHIVE_TESTID = "archive-button";
+
+const ICON_SIZE = 18;
+
+QuestionActionButtons.propTypes = {
+ canWrite: PropTypes.bool.isRequired,
+ onOpenModal: PropTypes.func.isRequired,
+};
+
+export default QuestionActionButtons;
+
+function QuestionActionButtons({ canWrite, onOpenModal }) {
+ return (
+
+ {canWrite && (
+
+ onOpenModal(MODAL_TYPES.EDIT)}
+ data-testid={EDIT_TESTID}
+ />
+
+ )}
+
+ onOpenModal(MODAL_TYPES.ADD_TO_DASHBOARD)}
+ data-testid={ADD_TO_DASH_TESTID}
+ />
+
+ {canWrite && (
+
+ onOpenModal(MODAL_TYPES.MOVE)}
+ data-testid={MOVE_TESTID}
+ />
+
+ )}
+ {canWrite && (
+
+ onOpenModal(MODAL_TYPES.CLONE)}
+ data-testid={CLONE_TESTID}
+ />
+
+ )}
+ {canWrite && (
+
+ onOpenModal(MODAL_TYPES.ARCHIVE)}
+ data-testid={ARCHIVE_TESTID}
+ />
+
+ )}
+
+ );
+}
diff --git a/frontend/src/metabase/query_builder/components/QuestionActionButtons.styled.jsx b/frontend/src/metabase/query_builder/components/QuestionActionButtons.styled.jsx
new file mode 100644
index 000000000000..0c258c624d30
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/QuestionActionButtons.styled.jsx
@@ -0,0 +1,8 @@
+import styled from "styled-components";
+
+export const Container = styled.div`
+ display: flex;
+ align-items: center;
+ column-gap: 0.3rem;
+ margin-top: 8px;
+`;
diff --git a/frontend/src/metabase/query_builder/components/QuestionActivityTimeline.jsx b/frontend/src/metabase/query_builder/components/QuestionActivityTimeline.jsx
new file mode 100644
index 000000000000..82b08f840947
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/QuestionActivityTimeline.jsx
@@ -0,0 +1,87 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { t } from "ttag";
+import { connect } from "react-redux";
+import _ from "underscore";
+
+import { getRevisionEventsForTimeline } from "metabase/lib/revisions";
+import { revertToRevision } from "metabase/query_builder/actions";
+
+import Revision from "metabase/entities/revisions";
+import Timeline from "metabase/components/Timeline";
+import {
+ SidebarSectionHeader,
+ RevertButton,
+} from "./QuestionActivityTimeline.styled";
+
+export default _.compose(
+ Revision.loadList({
+ query: (state, props) => ({
+ model_type: "card",
+ model_id: props.question.id(),
+ }),
+ wrapped: true,
+ }),
+ connect(
+ null,
+ {
+ revertToRevision,
+ },
+ ),
+)(QuestionActivityTimeline);
+
+RevisionEventFooter.propTypes = {
+ revision: PropTypes.object.isRequired,
+ onRevisionClick: PropTypes.func.isRequired,
+};
+
+function RevisionEventFooter({ revision, onRevisionClick }) {
+ return (
+
+ onRevisionClick(revision)}
+ normalText={t`Revert`}
+ activeText={t`Reverting…`}
+ failedText={t`Revert failed`}
+ successText={t`Reverted`}
+ />
+
+ );
+}
+
+QuestionActivityTimeline.propTypes = {
+ question: PropTypes.object.isRequired,
+ className: PropTypes.string,
+ revisions: PropTypes.array,
+ revertToRevision: PropTypes.func.isRequired,
+};
+
+export function QuestionActivityTimeline({
+ question,
+ className,
+ revisions,
+ revertToRevision,
+}) {
+ const canWrite = question.canWrite();
+ const revisionEvents = getRevisionEventsForTimeline(revisions, canWrite);
+
+ return (
+
+ {t`History`}
+ {
+ const { isRevertable, revision } = item;
+ if (isRevertable) {
+ return (
+
+ );
+ }
+ }}
+ />
+
+ );
+}
diff --git a/frontend/src/metabase/query_builder/components/QuestionActivityTimeline.styled.jsx b/frontend/src/metabase/query_builder/components/QuestionActivityTimeline.styled.jsx
new file mode 100644
index 000000000000..9c9091e93e15
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/QuestionActivityTimeline.styled.jsx
@@ -0,0 +1,37 @@
+import styled from "styled-components";
+import { color } from "metabase/lib/colors";
+import ActionButton from "metabase/components/ActionButton";
+import Button from "metabase/components/Button";
+
+export const SidebarSectionHeader = styled.div`
+ color: ${color("text-medium")};
+ font-weight: bold;
+ padding-bottom: 1rem;
+`;
+
+export const RequestButton = styled(Button)`
+ padding: 0;
+ border: none;
+ color: ${props => color(props.color)};
+
+ &:hover {
+ text-decoration: underline;
+ background-color: transparent;
+ color: ${props => color(props.color)};
+ }
+`;
+
+export const RevertButton = styled(ActionButton).attrs({
+ successClassName: "",
+ failedClassName: "",
+})`
+ padding: 0;
+ border: none;
+ color: ${color("text-dark")};
+ font-size: 0.875em;
+
+ &:hover {
+ background-color: transparent;
+ color: ${color("accent3")};
+ }
+`;
diff --git a/frontend/src/metabase/query_builder/components/SavedQuestionHeaderButton.jsx b/frontend/src/metabase/query_builder/components/SavedQuestionHeaderButton.jsx
new file mode 100644
index 000000000000..8fbd9bc82d66
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/SavedQuestionHeaderButton.jsx
@@ -0,0 +1,28 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+import { HeaderButton } from "./SavedQuestionHeaderButton.styled";
+
+export default SavedQuestionHeaderButton;
+
+SavedQuestionHeaderButton.propTypes = {
+ className: PropTypes.string,
+ question: PropTypes.object.isRequired,
+ onClick: PropTypes.func.isRequired,
+ isActive: PropTypes.bool.isRequired,
+};
+
+function SavedQuestionHeaderButton({ className, question, onClick, isActive }) {
+ return (
+
+ {question.displayName()}
+
+ );
+}
diff --git a/frontend/src/metabase/query_builder/components/SavedQuestionHeaderButton.styled.jsx b/frontend/src/metabase/query_builder/components/SavedQuestionHeaderButton.styled.jsx
new file mode 100644
index 000000000000..aa484586f90d
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/SavedQuestionHeaderButton.styled.jsx
@@ -0,0 +1,16 @@
+import styled from "styled-components";
+import { color } from "metabase/lib/colors";
+
+import Button from "metabase/components/Button";
+
+export const HeaderButton = styled(Button)`
+ font-size: 1.25rem;
+ border: none;
+ padding: 0.25rem 0.25rem;
+ color: ${props => (props.isActive ? color("brand") : "unset")};
+ background-color: ${props => (props.isActive ? color("bg-light") : "unset")};
+
+ .Icon-chevrondown {
+ height: 13px;
+ }
+`;
diff --git a/frontend/src/metabase/query_builder/components/view/QuestionEntityMenu.jsx b/frontend/src/metabase/query_builder/components/view/QuestionEntityMenu.jsx
deleted file mode 100644
index 013f7e73b361..000000000000
--- a/frontend/src/metabase/query_builder/components/view/QuestionEntityMenu.jsx
+++ /dev/null
@@ -1,53 +0,0 @@
-/* eslint-disable react/prop-types */
-import React from "react";
-import { t } from "ttag";
-
-import EntityMenu from "metabase/components/EntityMenu";
-
-import cx from "classnames";
-
-export default function QuestionEntityMenu({
- className,
- question,
- onOpenModal,
-}) {
- const canWrite = question && question.canWrite();
- return (
- onOpenModal("edit"),
- },
- {
- icon: "history",
- title: t`View revision history`,
- action: () => onOpenModal("history"),
- },
- {
- icon: "add_to_dash",
- title: t`Add to dashboard`,
- action: () => onOpenModal("add-to-dashboard"),
- },
- canWrite && {
- icon: "move",
- title: t`Move`,
- action: () => onOpenModal("move"),
- },
- canWrite && {
- icon: "clone",
- title: t`Duplicate this question`,
- action: () => onOpenModal("clone"),
- },
- canWrite && {
- icon: "archive",
- title: `Archive`,
- action: () => onOpenModal("archive"),
- },
- ]}
- />
- );
-}
diff --git a/frontend/src/metabase/query_builder/components/view/QuestionFilters.jsx b/frontend/src/metabase/query_builder/components/view/QuestionFilters.jsx
index 6119f1117e23..5ca0668accaa 100644
--- a/frontend/src/metabase/query_builder/components/view/QuestionFilters.jsx
+++ b/frontend/src/metabase/query_builder/components/view/QuestionFilters.jsx
@@ -43,7 +43,7 @@ export default function QuestionFilters({
) : isShowingChartTypeSidebar ? (
+ ) : isShowingQuestionDetailsSidebar ? (
+
) : null;
const rightSideBar =
diff --git a/frontend/src/metabase/query_builder/components/view/ViewHeader.jsx b/frontend/src/metabase/query_builder/components/view/ViewHeader.jsx
index 2a24796e7954..bcec8107ab8b 100644
--- a/frontend/src/metabase/query_builder/components/view/ViewHeader.jsx
+++ b/frontend/src/metabase/query_builder/components/view/ViewHeader.jsx
@@ -4,18 +4,16 @@ import { t } from "ttag";
import cx from "classnames";
import { Box } from "grid-styled";
-import Icon from "metabase/components/Icon";
import Link from "metabase/components/Link";
import ButtonBar from "metabase/components/ButtonBar";
import CollectionBadge from "metabase/questions/components/CollectionBadge";
import LastEditInfoLabel from "metabase/components/LastEditInfoLabel";
-
+import SavedQuestionHeaderButton from "metabase/query_builder/components/SavedQuestionHeaderButton";
import ViewSection, { ViewHeading, ViewSubHeading } from "./ViewSection";
import ViewButton from "metabase/query_builder/components/view/ViewButton";
import QuestionDataSource from "./QuestionDataSource";
import QuestionDescription from "./QuestionDescription";
-import QuestionEntityMenu from "./QuestionEntityMenu";
import QuestionLineage from "./QuestionLineage";
import QuestionPreviewToggle from "./QuestionPreviewToggle";
import QuestionNotebookButton from "./QuestionNotebookButton";
@@ -25,6 +23,7 @@ import { QuestionSummarizeWidget } from "./QuestionSummaries";
import NativeQueryButton from "./NativeQueryButton";
import RunButtonWithTooltip from "../RunButtonWithTooltip";
+import { SavedQuestionHeaderButtonContainer } from "./ViewHeader.styled";
import StructuredQuery from "metabase-lib/lib/queries/StructuredQuery";
@@ -44,6 +43,7 @@ const viewTitleHeaderPropTypes = {
isNativeEditorOpen: PropTypes.bool,
isShowingFilterSidebar: PropTypes.bool,
isShowingSummarySidebar: PropTypes.bool,
+ isShowingQuestionDetailsSidebar: PropTypes.bool,
runQuestionQuery: PropTypes.func,
cancelQuery: PropTypes.func,
@@ -53,6 +53,8 @@ const viewTitleHeaderPropTypes = {
onCloseSummary: PropTypes.func,
onAddFilter: PropTypes.func,
onCloseFilter: PropTypes.func,
+ onOpenQuestionDetails: PropTypes.func,
+ onCloseQuestionDetails: PropTypes.func,
isPreviewable: PropTypes.bool,
isPreviewing: PropTypes.bool,
@@ -114,10 +116,12 @@ export class ViewTitleHeader extends React.Component {
isShowingFilterSidebar,
onAddFilter,
onCloseFilter,
+ isShowingQuestionDetailsSidebar,
+ onOpenQuestionDetails,
+ onCloseQuestionDetails,
} = this.props;
const { isFiltersExpanded } = this.state;
const isShowingNotebook = queryBuilderMode === "notebook";
- const description = question.description();
const lastEditInfo = question.lastEditInfo();
const isStructured = question.isStructured();
@@ -142,41 +146,34 @@ export class ViewTitleHeader extends React.Component {
{isSaved ? (
-
- {question.displayName()}
-
- {description && (
-
+
- )}
-
+
{lastEditInfo && (
onOpenModal("history")}
/>
)}
-
+
- {QuestionDataSource.shouldRender({ question }) && (
- •
- )}
-
{QuestionDataSource.shouldRender({ question }) && (
diff --git a/frontend/src/metabase/query_builder/components/view/ViewHeader.styled.jsx b/frontend/src/metabase/query_builder/components/view/ViewHeader.styled.jsx
new file mode 100644
index 000000000000..80a66d3f67f9
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/view/ViewHeader.styled.jsx
@@ -0,0 +1,6 @@
+import styled from "styled-components";
+
+export const SavedQuestionHeaderButtonContainer = styled.div`
+ position: relative;
+ right: 0.38rem;
+`;
diff --git a/frontend/src/metabase/query_builder/components/view/ViewSidebar.jsx b/frontend/src/metabase/query_builder/components/view/ViewSidebar.jsx
index 3969a19c1360..41d6018910f0 100644
--- a/frontend/src/metabase/query_builder/components/view/ViewSidebar.jsx
+++ b/frontend/src/metabase/query_builder/components/view/ViewSidebar.jsx
@@ -1,8 +1,6 @@
/* eslint-disable react/prop-types */
import React from "react";
-
import cx from "classnames";
-
import { Motion, spring } from "react-motion";
const SPRING_CONFIG = { stiffness: 200, damping: 26 };
@@ -19,14 +17,14 @@ const ViewSideBar = ({ left, right, width = 355, isOpen, children }) => (
{motionStyle => (
|