Skip to content
Snippets Groups Projects
Unverified Commit 8876f689 authored by Kyle Doherty's avatar Kyle Doherty Committed by GitHub
Browse files

Revert Data Reference removal (#32695)


* revert dr removal

* revert some more code that the data reference needed

* Bring back `db_metadata` endpoint reference

* model test fixes

---------

Co-authored-by: default avatarRyan Laurie <iethree@gmail.com>
Co-authored-by: default avatarNemanja <31325167+nemanjaglumac@users.noreply.github.com>
parent 895305ba
No related branches found
No related tags found
No related merge requests found
Showing
with 799 additions and 3 deletions
......@@ -59,7 +59,6 @@ describe("scenarios > admin > datamodel > editor", () => {
});
});
// QUESTION - can we check update in the admin instead?
it("should allow changing the table description", () => {
visitTableMetadata();
setValueAndBlurInput(ORDERS_DESCRIPTION, "New description");
......@@ -67,6 +66,12 @@ describe("scenarios > admin > datamodel > editor", () => {
cy.findByDisplayValue("New description").should("be.visible");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Updated Table description").should("be.visible");
cy.visit(`/reference/databases/${SAMPLE_DB_ID}/tables/${ORDERS_ID}`);
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Orders").should("be.visible");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("New description").should("be.visible");
});
it("should allow clearing the table description", () => {
......@@ -75,6 +80,12 @@ describe("scenarios > admin > datamodel > editor", () => {
cy.wait("@updateTable");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Updated Table description").should("be.visible");
cy.visit(`/reference/databases/${SAMPLE_DB_ID}/tables/${ORDERS_ID}`);
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Orders").should("be.visible");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("No description yet").should("be.visible");
});
it("should allow changing the table visibility", () => {
......@@ -137,6 +148,14 @@ describe("scenarios > admin > datamodel > editor", () => {
.should("be.visible");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Updated Total").should("be.visible");
cy.visit(
`/reference/databases/${SAMPLE_DB_ID}/tables/${ORDERS_ID}/fields/${ORDERS.TOTAL}`,
);
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Total").should("be.visible");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("New description").should("be.visible");
});
it("should allow clearing the field description", () => {
......@@ -147,6 +166,14 @@ describe("scenarios > admin > datamodel > editor", () => {
cy.wait("@updateField");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Updated Total").should("be.visible");
cy.visit(
`/reference/databases/${SAMPLE_DB_ID}/tables/${ORDERS_ID}/fields/${ORDERS.TOTAL}`,
);
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Total").should("be.visible");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("No description yet").should("be.visible");
});
it("should allow changing the field visibility", () => {
......@@ -319,6 +346,14 @@ describe("scenarios > admin > datamodel > editor", () => {
cy.findByDisplayValue("New description").should("be.visible");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Updated Total").should("be.visible");
cy.visit(
`/reference/databases/${SAMPLE_DB_ID}/tables/${ORDERS_ID}/fields/${ORDERS.TOTAL}`,
);
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Total").should("be.visible");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("New description").should("be.visible");
});
it("should allow changing the field visibility", () => {
......
......@@ -5,6 +5,8 @@ import {
openOrdersTable,
visualize,
summarize,
filter,
filterField,
} from "e2e/support/helpers";
import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
import { createMetric } from "e2e/support/helpers/e2e-table-metadata-helpers";
......@@ -82,6 +84,16 @@ describe("scenarios > admin > datamodel > metrics", () => {
);
});
it("should show how to create metrics", () => {
cy.visit("/reference/metrics");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText(
"Metrics are the official numbers that your team cares about",
);
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Learn how to create metrics");
});
it("custom expression aggregation should work in metrics (metabase#22700)", () => {
cy.intercept("POST", "/api/dataset").as("dataset");
......@@ -136,6 +148,48 @@ describe("scenarios > admin > datamodel > metrics", () => {
});
});
it("should show no questions based on a new metric", () => {
cy.visit("/reference/metrics/1/questions");
cy.findAllByText("Questions about orders < 100");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Loading...");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Loading...").should("not.exist");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText(
"Questions about this metric will appear here as they're added",
);
});
it("should see a newly asked question in its questions list", () => {
// Ask a new qustion
cy.visit("/reference/metrics/1/questions");
cy.get(".full").find(".Button").click();
filter();
filterField("Total", {
placeholder: "min",
value: "50",
});
cy.findByTestId("apply-filters").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Save").click();
cy.findAllByText("Save").last().click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Not now").click();
// Check the list
cy.visit("/reference/metrics/1/questions");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Our analysis").should("not.exist");
cy.findAllByText("Questions about orders < 100");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText(
"Orders, orders < 100, Filtered by Total is greater than or equal to 50",
);
});
it("should show the metric detail view for a specific id", () => {
cy.visit("/admin/datamodel/metric/1");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
......
// Ported from `segments.e2e.spec.js`
import { restore, popover, modal } from "e2e/support/helpers";
import {
restore,
popover,
modal,
filter,
filterField,
} from "e2e/support/helpers";
import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
import { createSegment } from "e2e/support/helpers/e2e-table-metadata-helpers";
......@@ -50,6 +56,14 @@ describe("scenarios > admin > datamodel > segments", () => {
cy.findByText("Custom Expression");
});
});
it("should show no segments", () => {
cy.visit("/reference/segments");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Segments are interesting subsets of tables");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Learn how to create segments");
});
});
describe("with segment", () => {
......@@ -69,6 +83,29 @@ describe("scenarios > admin > datamodel > segments", () => {
});
});
it("should show the segment fields list and detail view", () => {
// In the list
cy.visit("/reference/segments");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText(SEGMENT_NAME);
// Detail view
cy.visit("/reference/segments/1");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Description");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("See this segment");
// Segment fields
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Fields in this segment").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("See this segment").should("not.exist");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText(`Fields in ${SEGMENT_NAME}`);
cy.findAllByText("Discount");
});
it("should show up in UI list", () => {
cy.visit("/admin/datamodel/segments");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
......@@ -85,6 +122,46 @@ describe("scenarios > admin > datamodel > segments", () => {
cy.findByText("Preview");
});
it("should show no questions based on a new segment", () => {
cy.visit("/reference/segments/1/questions");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText(`Questions about ${SEGMENT_NAME}`);
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText(
"Questions about this segment will appear here as they're added",
);
});
it("should see a newly asked question in its questions list", () => {
// Ask question
cy.visit("/reference/segments/1/questions");
cy.get(".full .Button").click();
cy.findAllByText("37.65");
filter();
filterField("Product ID", {
value: "14",
});
cy.findByTestId("apply-filters").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Product ID is 14");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Save").click();
cy.findAllByText("Save").last().click();
// Check list
cy.visit("/reference/segments/1/questions");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText(
"Questions about this segment will appear here as they're added",
).should("not.exist");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText(
`Orders, Filtered by ${SEGMENT_NAME} and Product ID equals 14`,
);
});
it("should update that segment", () => {
cy.visit("/admin");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
......
......@@ -14,6 +14,10 @@ describe("scenarios > browse data", () => {
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText(/^Our data$/i);
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Learn about our data").click();
cy.location("pathname").should("eq", "/reference/databases");
cy.go("back");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Sample Database").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Products").click();
......
import { popover, restore, startNewQuestion } from "e2e/support/helpers";
describe("scenarios > reference > databases", () => {
beforeEach(() => {
restore();
cy.signInAsAdmin();
});
it("should see the listing", () => {
cy.visit("/reference/databases");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.contains("Sample Database");
});
xit("should let the user navigate to details", () => {
cy.visit("/reference/databases");
cy.contains("Sample Database").click();
cy.contains("Why this database is interesting");
});
it("should let an admin edit details about the database", () => {
cy.visit("/reference/databases/1");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.contains("Edit").click();
// Q - is there any cleaner way to get a nearby element without having to know the DOM?
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.contains("Description")
.parent()
.parent()
.find("textarea")
.type("A pretty ok store");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.contains("Save").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.contains("A pretty ok store");
});
it("should let an admin start to edit and cancel without saving", () => {
cy.visit("/reference/databases/1");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.contains("Edit").click();
// Q - is there any cleaner way to get a nearby element without having to know the DOM?
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.contains("Why this")
.parent()
.parent()
.find("textarea")
.type("Turns out it's not");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.contains("Cancel").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.contains("Turns out").should("have.length", 0);
});
it("should let an admin edit the database name", () => {
cy.visit("/reference/databases/1");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.contains("Edit").click();
cy.get(".wrapper input").clear().type("My definitely profitable business");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.contains("Save").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.contains("My definitely profitable business");
});
describe("multiple databases sorting order", () => {
beforeEach(() => {
["d", "b", "a", "c"].forEach(name => {
cy.addH2SampleDatabase({ name });
});
});
it.skip("should sort data reference database list (metabase#15598)", () => {
cy.visit("/browse");
checkReferenceDatabasesOrder();
cy.visit("/reference/databases/");
checkReferenceDatabasesOrder();
});
it("should sort databases in new UI based question data selection popover", () => {
checkQuestionSourceDatabasesOrder();
});
it.skip("should sort databases in new native question data selection popover", () => {
checkQuestionSourceDatabasesOrder("Native query");
});
});
});
function checkReferenceDatabasesOrder() {
cy.get("[class*=Card]").as("databaseCard").first().should("have.text", "a");
cy.get("@databaseCard").last().should("have.text", "Sample Database");
}
function checkQuestionSourceDatabasesOrder(question_type) {
// Last item is "Saved Questions" for UI based questions so we have to check for the one before that (-2), and the last one for "Native" (-1)
const lastDatabaseIndex = question_type === "Native query" ? -1 : -2;
const selector =
question_type === "Native query"
? ".List-item-title"
: ".List-section-title";
startNewQuestion();
popover().within(() => {
cy.get(selector).as("databaseName").first().should("have.text", "a");
cy.get("@databaseName")
.eq(lastDatabaseIndex)
.should("have.text", "Sample Database");
});
}
import { restore } from "e2e/support/helpers";
import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
const { ORDERS, ORDERS_ID } = SAMPLE_DATABASE;
describe("scenarios > reference > metrics", () => {
const METRIC_NAME = "orders < 100";
const METRIC_DESCRIPTION = "Count of orders with a total under $100.";
beforeEach(() => {
restore();
cy.signInAsAdmin();
cy.request("POST", "/api/metric", {
definition: {
aggregation: ["count"],
filter: ["<", ["field", ORDERS.TOTAL, null], 100],
"source-table": ORDERS_ID,
},
name: METRIC_NAME,
description: METRIC_DESCRIPTION,
table_id: ORDERS_ID,
});
});
it("should see the listing", () => {
cy.visit("/reference/metrics");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText(METRIC_NAME);
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText(METRIC_DESCRIPTION);
});
it("should let the user navigate to details", () => {
cy.visit("/reference/metrics");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText(METRIC_NAME).click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Why this metric is interesting");
});
it("should let an admin edit details about the metric", () => {
cy.visit("/reference/metrics");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText(METRIC_NAME).click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Edit").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Description")
.parent()
.parent()
.find("textarea")
.clear()
.type("Count of orders under $100");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Save").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Reason for changes")
.parent()
.parent()
.find("textarea")
.type("Renaming the description");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Save changes").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Count of orders under $100");
});
it("should let an admin start to edit and cancel without saving", () => {
cy.visit("/reference/metrics");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText(METRIC_NAME).click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Edit").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Why this metric is interesting")
.parent()
.parent()
.find("textarea")
.type("Because it's very nice");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Cancel").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Because it's very nice").should("have.length", 0);
});
it("should have different URI while editing the metric", () => {
cy.visit("/reference/metrics");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText(METRIC_NAME).click();
cy.url().should("match", /\/reference\/metrics\/\d+$/);
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Edit").click();
cy.url().should("match", /\/reference\/metrics\/\d+\/edit$/);
});
});
import { popover, restore } from "e2e/support/helpers";
describe("issue 5276", () => {
beforeEach(() => {
restore();
cy.signInAsAdmin();
cy.intercept("PUT", "/api/field/*").as("updateField");
});
it("should allow removing the field type (metabase#5276)", () => {
cy.visit("/reference/databases");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Sample Database").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Tables in Sample Database").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Products").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Fields in this table").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Edit").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Score").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
popover().within(() => cy.findByText("No field type").click());
cy.button("Save").click();
cy.wait("@updateField");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Score").should("not.exist");
});
});
......@@ -21,7 +21,7 @@ const setup = () => {
renderWithProviders(<SsoButton />, { storeInitialState: state });
};
describe("SsoButton", () => {
describe("SSOButton", () => {
it("should login immediately when embedded", async () => {
jest.spyOn(domUtils, "redirect").mockImplementation(() => undefined);
......
/* eslint-disable react/prop-types */
import { t } from "ttag";
import BrowserCrumbs from "metabase/components/BrowserCrumbs";
import { Icon } from "metabase/core/components/Icon";
import Link from "metabase/core/components/Link";
import { ANALYTICS_CONTEXT } from "metabase/browse/constants";
import { BrowseHeaderContent, BrowseHeaderRoot } from "./BrowseHeader.styled";
......@@ -9,6 +13,20 @@ export default function BrowseHeader({ crumbs }) {
<BrowseHeaderRoot>
<BrowseHeaderContent>
<BrowserCrumbs crumbs={crumbs} analyticsContext={ANALYTICS_CONTEXT} />
<div className="flex flex-align-right">
<Link
className="flex flex-align-right"
to="reference"
data-metabase-event="NavBar;Reference"
>
<div className="flex align-center text-medium text-brand-hover">
<Icon className="flex align-center" size={14} name="reference" />
<span className="ml1 flex align-center text-bold">
{t`Learn about our data`}
</span>
</div>
</Link>
</div>
</BrowseHeaderContent>
</BrowseHeaderRoot>
);
......
......@@ -135,6 +135,16 @@ const TableBrowserItemButtons = ({ tableId, dbId, xraysEnabled }) => {
/>
</TableActionLink>
)}
<TableActionLink
to={`/reference/databases/${dbId}/tables/${tableId}`}
data-metabase-event={`${ANALYTICS_CONTEXT};Table Item;Reference Click`}
>
<Icon
name="reference"
tooltip={t`Learn about this table`}
color={color("text-medium")}
/>
</TableActionLink>
</Fragment>
);
};
......
:root {
--title-color: var(--color-text-dark);
--subtitle-color: var(--color-text-medium);
--muted-color: var(--color-text-light);
}
:local(.list) {
composes: ml-auto mr-auto from "style";
}
:local(.list-wrapper) {
composes: ml-auto mr-auto from "style";
}
:local(.list) a {
text-decoration: none;
}
:local(.header) {
composes: flex flex-row from "style";
composes: mt4 mb2 from "style";
color: var(--title-color);
font-size: 24px;
min-height: 48px;
}
:local(.headerBody) {
composes: flex flex-full border-bottom from "style";
align-items: center;
height: 100%;
border-color: var(--color-brand);
}
:local(.headerLink) {
composes: text-brand ml2 flex-no-shrink from "style";
font-size: 14px;
}
:local(.headerButton) {
composes: flex ml1 align-center from "style";
font-size: 14px;
}
:local(.empty) {
composes: full flex justify-center from "style";
padding-top: 75px;
}
:local(.item) {
composes: flex align-center from "style";
composes: relative from "style";
}
:local(.itemBody) {
composes: flex-full from "style";
max-width: 100%;
}
:local(.itemTitle) {
composes: text-bold from "style";
max-width: 100%;
overflow: hidden;
}
:local(.itemName) {
composes: text-brand-hover mr1 from "style";
max-width: 100%;
overflow: hidden;
}
:local(.itemSubtitle) {
color: var(--subtitle-color);
max-width: 600px;
font-size: 14px;
}
:local(.itemSubtitleLight) {
composes: text-light from "style";
font-size: 14px;
}
:local(.itemSubtitleBold) {
color: var(--title-color);
}
:local(.icons) {
composes: flex flex-row align-center from "style";
}
:local(.leftIcons) {
composes: flex-no-shrink flex align-self-start mr2 from "style";
composes: icons;
}
:local(.rightIcons) {
composes: icons;
}
:local(.itemIcons) {
composes: leftIcons;
padding-top: 4px;
}
:local(.extraIcons) {
composes: icons;
composes: absolute top full-height from "style";
right: -40px;
}
/* hack fix for IE 11 which was hiding the archive icon */
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
:local(.extraIcons) {
composes: icons;
}
}
:local(.icon) {
composes: relative from "style";
color: var(--muted-color);
}
:local(.item) :local(.icon) {
visibility: hidden;
}
:local(.item):hover :local(.icon) {
visibility: visible;
}
:local(.icon):hover {
color: var(--color-brand);
}
/* ITEM CHECKBOX */
:local(.itemCheckbox) {
composes: icon;
display: none;
visibility: visible !important;
margin-left: 10px;
}
:local(.item):hover :local(.itemCheckbox),
:local(.item.selected) :local(.itemCheckbox) {
display: inline;
}
:local(.item.selected) :local(.itemCheckbox) {
color: var(--color-brand);
}
/* ITEM ICON */
:local(.itemIcon) {
composes: icon;
visibility: visible !important;
composes: relative from "style";
}
:local(.item):hover :local(.itemIcon),
:local(.item.selected) :local(.itemIcon) {
display: none;
}
/* CHART ICON */
:local(.chartIcon) {
composes: icon;
visibility: visible !important;
composes: relative from "style";
}
/* ACTION ICONS */
:local(.tagIcon),
:local(.favoriteIcon),
:local(.archiveIcon) {
composes: icon;
composes: mx1 from "style";
}
:local(.trigger) {
line-height: 0;
}
/* eslint "react/prop-types": "warn" */
import { memo } from "react";
import PropTypes from "prop-types";
import S from "./List.css";
const List = ({ children }) => <ul className={S.list}>{children}</ul>;
List.propTypes = {
children: PropTypes.any.isRequired,
};
export default memo(List);
export { default } from "./List";
/* eslint "react/prop-types": "warn" */
import { memo } from "react";
import PropTypes from "prop-types";
import { Link } from "react-router";
import cx from "classnames";
import Ellipsified from "metabase/core/components/Ellipsified";
import Card from "metabase/components/Card";
import S from "metabase/components/List/List.css";
import { Icon } from "metabase/core/components/Icon";
const ListItem = ({ name, description, placeholder, url, icon }) => (
<li className="relative">
<Link to={url} className="text-brand-hover">
<Card hoverable className="mb2 p3 bg-white rounded bordered">
<div className={cx(S.item)}>
<div className={S.itemIcons}>
{icon && <Icon className={S.chartIcon} name={icon} size={16} />}
</div>
<div className={S.itemBody}>
<div className={S.itemTitle}>
<Ellipsified
className={S.itemName}
tooltip={name}
tooltipMaxWidth="100%"
>
<h3>{name}</h3>
</Ellipsified>
</div>
{(description || placeholder) && (
<div className={cx(S.itemSubtitle)}>
{description || placeholder}
</div>
)}
</div>
</div>
</Card>
</Link>
</li>
);
ListItem.propTypes = {
name: PropTypes.string.isRequired,
url: PropTypes.string,
description: PropTypes.string,
placeholder: PropTypes.string,
icon: PropTypes.string,
};
export default memo(ListItem);
import { Route } from "react-router";
import { screen, getIcon, renderWithProviders } from "__support__/ui";
import ListItem from "./ListItem";
const ITEM_NAME = "Table Foo";
const ITEM_DESCRIPTION = "Nice table description.";
function setup({ name, ...opts }) {
return renderWithProviders(
<Route path="/" component={() => <ListItem name={name} {...opts} />} />,
{ withRouter: true },
);
}
describe("ListItem", () => {
it("should render", () => {
setup({
name: ITEM_NAME,
description: ITEM_DESCRIPTION,
icon: "table",
url: "/foo",
});
expect(screen.getByText(ITEM_NAME)).toBeInTheDocument();
expect(screen.getByText(ITEM_DESCRIPTION)).toBeInTheDocument();
expect(getIcon("table")).toBeInTheDocument();
expect(screen.getByRole("link")).toHaveProperty(
"href",
"http://localhost/foo",
);
});
it("should render with just the name", () => {
setup({ name: ITEM_NAME });
expect(screen.getByText(ITEM_NAME)).toBeInTheDocument();
});
it("should display the placeholder if there's no description", () => {
setup({ name: ITEM_NAME, placeholder: "Placeholder text" });
expect(screen.getByText(ITEM_NAME)).toBeInTheDocument();
expect(screen.getByText("Placeholder text")).toBeInTheDocument();
});
it("should display the description and omit the placeholder if both are present", () => {
setup({
name: ITEM_NAME,
description: ITEM_DESCRIPTION,
placeholder: "Placeholder text",
});
expect(screen.getByText(ITEM_NAME)).toBeInTheDocument();
expect(screen.getByText(ITEM_DESCRIPTION)).toBeInTheDocument();
expect(screen.queryByText("Placeholder text")).not.toBeInTheDocument();
});
});
export { default } from "./ListItem";
:local(.queryButton) {
composes: flex align-center no-decoration text-brand py1 from "style";
}
:local(.queryButtonText) {
composes: flex-full ml2 from "style";
max-width: 100%;
}
import { memo } from "react";
import PropTypes from "prop-types";
import { Link } from "react-router";
import cx from "classnames";
import { Icon } from "metabase/core/components/Icon";
import S from "./QueryButton.css";
const QueryButton = ({ className, text, icon, iconClass, onClick, link }) => (
<div className={className}>
<Link
className={cx(S.queryButton, "bg-light-hover px1 rounded")}
onClick={onClick}
to={link}
>
<Icon name={icon} />
<span className={S.queryButtonText}>{text}</span>
</Link>
</div>
);
QueryButton.propTypes = {
className: PropTypes.string,
icon: PropTypes.any.isRequired,
text: PropTypes.string.isRequired,
iconClass: PropTypes.string,
onClick: PropTypes.func,
link: PropTypes.string,
};
export default memo(QueryButton);
export { default } from "./QueryButton";
import _ from "underscore";
import { normalize } from "normalizr";
import { createSelector } from "@reduxjs/toolkit";
import { createEntity } from "metabase/lib/entities";
import * as Urls from "metabase/lib/urls";
import { color } from "metabase/lib/colors";
import {
fetchData,
createThunkAction,
compose,
withAction,
withCachedDataAndRequestState,
......@@ -41,6 +44,25 @@ const Databases = createEntity({
// ACTION CREATORS
objectActions: {
fetchDatabaseMetadata: createThunkAction(
FETCH_DATABASE_METADATA,
({ id }, { reload = false, params } = {}) =>
(dispatch, getState) =>
fetchData({
dispatch,
getState,
requestStatePath: ["metadata", "databases", id],
existingStatePath: ["metadata", "databases", id],
getData: async () => {
const databaseMetadata = await MetabaseApi.db_metadata({
dbId: id,
...params,
});
return normalize(databaseMetadata, DatabaseSchema);
},
reload,
}),
),
fetchIdFields: compose(
withAction(FETCH_DATABASE_IDFIELDS),
withCachedDataAndRequestState(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment