Skip to content

Commit

Permalink
# This is a combination of 6 commits.
Browse files Browse the repository at this point in the history
# This is the 1st commit message:

feat: validation db modal (#14832)

* split db modal file

* hook up available databases

* use new validation component
# This is the commit message #2:

feat: Icon Button (#14818)

* Creating IconButton

* Changed naming: logo is now icon

* Hard-coded inset values for ellipses

* Removed default SVG

* Fixed test

* Removed logo from test
# This is the commit message #3:

chore: Improves the native filters UI/UX - iteration 6 (#14932)


# This is the commit message #4:

fix: is_temporal should overwrite is_dttm (#14894)

* fix: is_temporal should overwrite is_dttm

* move up
# This is the commit message #5:

fix: time parser truncate to first day of year/month (#14945)


# This is the commit message #6:

hook up available databases
  • Loading branch information
eschutho authored and hughhhh committed Jun 2, 2021
1 parent fac6b7c commit 29a899d
Show file tree
Hide file tree
Showing 23 changed files with 717 additions and 236 deletions.
18 changes: 13 additions & 5 deletions superset-frontend/src/components/Form/LabeledErrorBoundInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,18 @@ import FormLabel from './FormLabel';
export interface LabeledErrorBoundInputProps {
label?: string;
validationMethods:
| { onBlur: (value: any) => string }
| { onChange: (value: any) => string };
| { onBlur: (value: any) => void }
| { onChange: (value: any) => void };
errorMessage: string | null;
helpText?: string;
required?: boolean;
id?: string;
classname?: string;
[x: string]: any;
}

const StyledInput = styled(Input)`
margin: 8px 0;
margin: ${({ theme }) => `${theme.gridUnit}px 0 ${theme.gridUnit * 2}px`};
`;

const alertIconStyles = (theme: SupersetTheme, hasError: boolean) => css`
Expand All @@ -60,6 +61,12 @@ const alertIconStyles = (theme: SupersetTheme, hasError: boolean) => css`
}
}`}
`;
const StyledFormGroup = styled('div')`
margin-bottom: ${({ theme }) => theme.gridUnit * 5}px;
.ant-form-item {
margin-bottom: 0;
}
`;

const LabeledErrorBoundInput = ({
label,
Expand All @@ -68,9 +75,10 @@ const LabeledErrorBoundInput = ({
helpText,
required = false,
id,
className,
...props
}: LabeledErrorBoundInputProps) => (
<>
<StyledFormGroup className={className}>
<FormLabel htmlFor={id} required={required}>
{label}
</FormLabel>
Expand All @@ -83,7 +91,7 @@ const LabeledErrorBoundInput = ({
>
<StyledInput {...props} {...validationMethods} />
</FormItem>
</>
</StyledFormGroup>
);

export default LabeledErrorBoundInput;
58 changes: 58 additions & 0 deletions superset-frontend/src/components/IconButton/IconButton.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import IconButton, { IconButtonProps } from '.';

export default {
title: 'IconButton',
component: IconButton,
};

export const InteractiveIconButton = (args: IconButtonProps) => (
<IconButton
buttonText={args.buttonText}
altText={args.altText}
icon={args.icon}
href={args.href}
target={args.target}
htmlType={args.htmlType}
/>
);

InteractiveIconButton.args = {
buttonText: 'This is the IconButton text',
altText: 'This is an example of non-default alt text',
href: 'https://preset.io/',
target: '_blank',
};

InteractiveIconButton.argTypes = {
icon: {
defaultValue: '/images/icons/sql.svg',
control: {
type: 'select',
options: [
'/images/icons/sql.svg',
'/images/icons/server.svg',
'/images/icons/image.svg',
'Click to see example alt text',
],
},
},
};
38 changes: 38 additions & 0 deletions superset-frontend/src/components/IconButton/IconButton.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { render, screen } from 'spec/helpers/testing-library';
import IconButton from 'src/components/IconButton';

const defaultProps = {
buttonText: 'This is the IconButton text',
icon: '/images/icons/sql.svg',
};

describe('IconButton', () => {
it('renders an IconButton', () => {
render(<IconButton {...defaultProps} />);

const icon = screen.getByRole('img');
const buttonText = screen.getByText(/this is the iconbutton text/i);

expect(icon).toBeVisible();
expect(buttonText).toBeVisible();
});
});
123 changes: 123 additions & 0 deletions superset-frontend/src/components/IconButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { styled } from '@superset-ui/core';
import Button from 'src/components/Button';
import { ButtonProps as AntdButtonProps } from 'antd/lib/button';

export interface IconButtonProps extends AntdButtonProps {
buttonText: string;
icon: string;
altText?: string;
}

const StyledButton = styled(Button)`
height: auto;
display: flex;
flex-direction: column;
padding: 0;
`;
const StyledImage = styled.div`
margin: ${({ theme }) => theme.gridUnit * 8}px 0;
padding: ${({ theme }) => theme.gridUnit * 4}px;
&:first-of-type {
margin-right: 0;
}
img {
width: fit-content;
&:first-of-type {
margin-right: 0;
}
}
`;

const StyledInner = styled.div`
max-height: calc(1.5em * 2);
overflow: hidden;
padding-right: 1rem;
position: relative;
white-space: break-spaces;
&::before {
content: '...';
inset-block-end: 0; /* "bottom" */
inset-inline-end: 8px; /* "right" */
position: absolute;
}
&::after {
background-color: ${({ theme }) => theme.colors.grayscale.light4};
content: '';
height: 1rem;
inset-inline-end: 8px; /* "right" */
position: absolute;
top: 4px;
width: 1rem;
}
`;

const StyledBottom = styled.div`
padding: ${({ theme }) => theme.gridUnit * 6}px
${({ theme }) => theme.gridUnit * 4}px;
border-radius: 0 0 ${({ theme }) => theme.borderRadius}px
${({ theme }) => theme.borderRadius}px;
background-color: ${({ theme }) => theme.colors.grayscale.light4};
width: 100%;
line-height: 1.5em;
overflow: hidden;
white-space: no-wrap;
text-overflow: ellipsis;
&:first-of-type {
margin-right: 0;
}
`;

const IconButton = styled(
({ icon, altText, buttonText, ...props }: IconButtonProps) => (
<StyledButton {...props}>
<StyledImage>
<img src={icon} alt={altText} />
</StyledImage>
<StyledBottom>
<StyledInner>{buttonText}</StyledInner>
</StyledBottom>
</StyledButton>
),
)`
text-transform: none;
background-color: ${({ theme }) => theme.colors.grayscale.light5};
font-weight: ${({ theme }) => theme.typography.weights.normal};
color: ${({ theme }) => theme.colors.grayscale.dark2};
border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
margin: 0;
width: 100%;
&:hover,
&:focus {
background-color: ${({ theme }) => theme.colors.grayscale.light5};
color: ${({ theme }) => theme.colors.grayscale.dark2};
border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
}
`;

export default IconButton;
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import Icon from 'src/components/Icon';
import { FilterRemoval } from './types';
import { REMOVAL_DELAY_SECS } from './utils';

export const FILTER_WIDTH = 200;
export const FILTER_WIDTH = 180;

export const StyledSpan = styled.span`
cursor: pointer;
Expand Down Expand Up @@ -115,6 +115,7 @@ const FilterTabsContainer = styled(LineEditableTabs)`
padding-right: ${theme.gridUnit}px;
padding-bottom: ${theme.gridUnit * 3}px;
padding-left: ${theme.gridUnit * 3}px;
width: 270px;
}
// extra selector specificity:
Expand Down Expand Up @@ -185,6 +186,8 @@ const FilterTabs: FC<FilterTabsProps> = ({
children,
}) => (
<FilterTabsContainer
id="native-filters-tabs"
type="editable-card"
tabPosition="left"
onChange={onChange}
activeKey={currentFilterId}
Expand All @@ -193,7 +196,20 @@ const FilterTabs: FC<FilterTabsProps> = ({
tabBarExtraContent={{
left: <StyledHeader>{t('Filters')}</StyledHeader>,
right: (
<StyledAddFilterBox onClick={() => onEdit('', 'add')}>
<StyledAddFilterBox
onClick={() => {
onEdit('', 'add');
setTimeout(() => {
const element = document.getElementById('native-filters-tabs');
if (element) {
const navList = element.getElementsByClassName(
'ant-tabs-nav-list',
)[0];
navList.scrollTop = navList.scrollHeight;
}
}, 0);
}}
>
<PlusOutlined />{' '}
<span data-test="add-filter-button" aria-label="Add filter">
{t('Add filter')}
Expand Down
Loading

0 comments on commit 29a899d

Please sign in to comment.