Access theme variables outside withStyles function (react-native-ui-kitten) - react-native-ui-kitten

I want to use theme variables to style my Icon accordingly. However i cant use style property to fill the Icon element of react-native-ui-kitten but instead have to use the fill property. How can I access theme variables outside of the withStyles function of react-native-ui-kitten

#xk2tm5ah5c
You can use theme property if you wrap your component into withStyles.
Here is an example code:
import React from 'react';
import { View } from 'react-native';
import { Button, Icon, withStyles } from 'react-native-ui-kitten';
const ScreenComponent = (props) => {
const iconColor = props.theme['color-primary-default'];
const FacebookIcon = (style) => (
<Icon {...style} fill={iconColor} name='facebook' />
);
return (
<View>
<Button icon={FacebookIcon}>LOGIN WITH FACEBOOK</Button>
</View>
);
};
export const Screen = withStyles(ScreenComponent);

I'm not quite sure I understand your question completely. Usually when you have a question, you should post some of your code for context.
Here is my answer assuming 'Theme variable' is a hash... Try string interpolation:
fill={`${theme.HEX_COLOR}`}

Related

With styled components how to pass theme color from Global Style to React Icons Context Provider?

Looking for a way to pass color from theme to React Icons. Theme is working correctly and I'm able to pass colors to my styled components. Here is the breakdown:
index.js:
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
// Theme
import { ThemeProvider } from 'styled-components'
import { GlobalStyles } from './theme/GlobalStyles'
import Theme from './theme/theme'
ReactDOM.render(
<ThemeProvider theme={Theme}>
<GlobalStyles />
<App />
</ThemeProvider>,
document.getElementById('root'),
)
app.js (stripped down):
<IconContext.Provider value={{ color: `${({ theme }) => theme.colors.white}` }}>
<FaTimes />
<FaBars />
</IconContext.Provider>
the equivalent of:
<IconContext.Provider value={{ color: `#fff` }}>
<FaTimes />
<FaBars />
</IconContext.Provider>
does work and I did try:
NavElements.js:
import { IconContext } from 'react-icons/lib'
export const NavProvider = styled(<IconContext.Provider>{children}</IconContext.Provider>)`
color: ${({ theme }) => theme.colors.color2};
`
app.js:
// <IconContext.Provider value={{ color: `#fff` }}>
<NavProvider>
// Further code
</NavProvider>
// </IconContext.Provider>
but I get a children error. Attempt came from reading Q&A Styled-components and react-icons <IconContext.Provider> component
Other Q&As I found with no luck:
How to Style React-Icons
react-icons apply a linear gradient
With a theme color in Styled Components how can I pass that color to React Icons Provider?
I haven't worked with react-icon but this might help
Take a look at getting the theme without styled components - there is also a hook
Your example
export const NavProvider = styled(<IconContext.Provider>{children}</IconContext.Provider>)`
color: ${({ theme }) => theme.colors.color2};
`
work because styled expects a HTML element or a React component
Your NavProvider could be something like (haven't tried this code but it should work)
import { useContext } from 'react';
import { ThemeContext } from 'styled-components';
export const NavProvider = ({children}) => {
const themeContext = useContext(ThemeContext);
return (
<IconContext.Provider value={{ color: themeContext.colors.color2 }}>
{children}
</IconContext.Provider>
);
};

How to inject Material-UI stylesheets into a jest/react-testing-library test?

It seems that if you don't inject Material-UI stylesheets into a jest/react-testing-library test then jsdom will fail to get the correct styles from your components (e.g. running getComputedStyle(component) will return the incorrect styles for the component).
How you properly setup a jest/react-testing-library test so that the styles are correctly injected into the test? I've already wrapped the components in a theme provider, which works fine.
As a workaround reinserting the whole head (or the element where JSS styles are injected) before assertion seems to apply styles correctly with both getComputedStyle() and react testing library's toHaveStyle():
import React from "react";
import "#testing-library/jest-dom/extend-expect";
import { render } from "#testing-library/react";
test("test my styles", () => {
const { getByTestId } = render(
<div data-testid="wrapper">
<MyButtonStyledWithJSS/>
</div>
);
const button = getByTestId("wrapper").firstChild;
document.head.innerHTML = document.head.innerHTML;
expect(button).toHaveStyle(`border-radius: 4px;`);
});
This will still fail though when you're using dynamic styles, like:
myButton: {
padding: props => props.spacing,
...
}
That's because JSS uses CSSStyleSheet.insertRule method to inject these styles, and it won't appear as a style node in the head. One solution to this issue is to hook into the browser's insertRule method and add incoming rules to the head as style tags. To extract all this into a function:
function mockStyleInjection() {
const defaultInsertRule = window.CSSStyleSheet.prototype.insertRule;
window.CSSStyleSheet.prototype.insertRule = function (rule, index) {
const styleElement = document.createElement("style");
const textNode = document.createTextNode(rule);
styleElement.appendChild(textNode);
document.head.appendChild(styleElement);
return defaultInsertRule.bind(this)(rule, index);
};
// cleanup function, which reinserts the head and cleans up method overwrite
return function applyJSSRules() {
window.CSSStyleSheet.prototype.insertRule = defaultInsertRule;
document.head.innerHTML = document.head.innerHTML;
};
}
Example usage of this function in our previous test:
import React from "react";
import "#testing-library/jest-dom/extend-expect";
import { render } from "#testing-library/react";
test("test my styles", () => {
const applyJSSRules = mockStyleInjection();
const { getByTestId } = render(
<div data-testid="wrapper">
<MyButtonStyledWithJSS spacing="8px"/>
</div>
);
const button = getByTestId("wrapper").firstChild;
applyJSSRules();
expect(button).toHaveStyle("border-radius: 4px;");
expect(button).toHaveStyle("padding: 8px;");
});
This ultimately seems like an issue with JSS and various browser implementations like jsdom and and Blink (at least in Chrome). You can see it in Chrome when trying to modify/enable/disable these style rules (you can't).
The behavior appears to be a result of the JSS library using the CSSOM insertRule API. There's a stylesheet generated in the DOM for the styles we expect in our component, but the tag is empty - it's just used to link the shadow CSS back to the DOM. The styles are never written to the inline stylesheet in the DOM, and as a result, the getComputedStyle method does not return the expected results.
There's an open issue to address this behavior and make development easier.
I switched my custom components to styled-components, which does not have some of these idiosyncrasies.
Material-UI is planning on transitioning soon as well.
You could add this to a custom render function. After rendering, the function pulls the styles out of cssom and puts them into a style tag. Here is an implementation:
let customRender = (ui, options) => {
let renderResult = render(ui, options);
let styleElement = document.createElement("style");
let styleText = "";
for (let styleSheet of document.styleSheets) {
for (let rule of styleSheet.cssRules) {
styleText += rule.cssText + "\n";
}
}
styleElement.textContent = styleText.slice(0, -1);
document.head.appendChild(styleElement);
// remove old style elements
let emptyStyleElements = document.head.querySelectorAll('style[data-jss=""]');
for (let element of emptyStyleElements) {
element.remove();
}
return renderResult;
}
I can't speak specifically to Material-UI stylesheets, but you can inject a stylesheet into rendered component:
import {render} from '#testing-library/react';
import fs from 'fs';
import path from 'path';
const stylesheetFile = fs.reactFileSync(path.resolve(__dirname, '../path-to-stylesheet'), 'utf-8');
const styleTag = document.createElement('style');
styleTag.type = 'text/css';
styleTag.innerHTML = stylesheetFile;
const rendered = render(<MyComponent>);
rendered.append(style);
You don't necessarily have to read from a file, you can use whatever text you want.

How to mount styles inside shadow root using cssinjs/jss

I'm trying to use https://material-ui.com/ components inside shadow dom, and need a way to inject those styles inside shadow dom. by default material-ui, which uses jss under the hood injects styles in the head of the page.
Is that even possible? Can anyone come with an example?
This is what my web component looks like, it is a web component that renders a react app that contains material-ui styles.
import * as React from 'react';
import { render } from 'react-dom';
import { StylesProvider, jssPreset } from '#material-ui/styles';
import { create } from 'jss';
import { App } from '#myApp/core';
class MyWebComponent extends HTMLElement {
connectedCallback() {
const shadowRoot = this.attachShadow({ mode: 'open' });
const mountPoint = document.createElement('span');
const reactRoot = shadowRoot.appendChild(mountPoint);
const jss = create({
...jssPreset(),
insertionPoint: reactRoot
});
render(
<StylesProvider jss={jss}>
<App />
</StylesProvider>,
mountPoint
);
}
}
customElements.define('my-web-commponent', MyWebComponent);
Setting the insertionPoint on jss to the actual react root inside the shadow root will tell jss to insert those styles inside that shadow root.
Using https://github.com/Wildhoney/ReactShadow to create shadow dom (you could also do it by hand as shown in previous answer), I created a small WrapperComponent that encapsulates the logic.
import root from 'react-shadow';
import {jssPreset, StylesProvider} from "#material-ui/styles";
import {create} from 'jss';
import React, {useState} from "react"
const WrappedJssComponent = ({children}) => {
const [jss, setJss] = useState(null);
function setRefAndCreateJss(headRef) {
if (headRef && !jss) {
const createdJssWithRef = create({...jssPreset(), insertionPoint: headRef})
setJss(createdJssWithRef)
}
}
return (
<root.div>
<head>
<style ref={setRefAndCreateJss}></style>
</head>
{jss &&
<StylesProvider jss={jss}>
{children}
</StylesProvider>
}
</root.div>
)
}
export default WrappedJssComponent
Then you just need to Wrap your app, or the part of your app you want to shadow inside <WrappedJssComponenent><YourComponent></YourComponent></WrappedJssComponenent>.
Be careful, some of the material-UI component won't work as usual (I had some trouble with
ClickAwayListener, maybe because it uses the parent dom, did not investigate more than that to be honest.
Popper, and everything that will try to use document.body as container will not have access to jss defined in shadow node. You should give an element inside the shadow dom as container.
There is also a whole page in the docs now (MaterialUI 5) that covers how to integrate MUI with a shadow-dom. You also might have to set Portal defaults not to target the dom. https://mui.com/material-ui/guides/shadow-dom/
When using #material-ui/core/CssBaseline with MUI, also emotion styles are being used. In order to support both legacy jss and emotion you can extend the accepted answer above with a CacheProvider like this:
import ReactDOM from 'react-dom/client'
import App from './App'
import createCache from '#emotion/cache'
import { CacheProvider } from '#emotion/react';
import { StylesProvider, jssPreset } from '#material-ui/styles';
import { create } from 'jss';
class ReportComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
const mountPoint = document.createElement('div');
const emotionPoint = this.shadowRoot!.appendChild(document.createElement('div'));
const emotionCache = createCache({
key: 'report-component',
container: emotionPoint
});
const reactRoot = this.shadowRoot!.appendChild(mountPoint);
const root = ReactDOM.createRoot(reactRoot);
const jss = create({
...jssPreset(),
insertionPoint: reactRoot
});
root.render(
<StylesProvider jss={jss}>
<CacheProvider value={emotionCache}>
<App />
</CacheProvider>
</StylesProvider>
);
}
}
customElements.define('report-component', ReportComponent);

Styling react-select with styled-components

I'm trying to change the color of the select-up-arrow and the color of the control when it's in focus, but without success. Have anyone done this using styled-components?
This applies to react-select#v2.*
The same ideas as #bamse answer can be applied to v2 of react-select. The problem is that in v2 they removed pre-determined class names unless you specify to add them in with the prop classNamePrefix. They also changed what the class names in general look like.
General solution is to make sure to add in the class names with the prop classNamePrefix, then use styled around ReactSelect and target classes within it.
import React from 'react';
import ReactSelect from 'react-select';
import styled from 'styled-components';
const ReactSelectElement = styled(ReactSelect)`
.react-select__indicator react-select__dropdown-indicator {
border-color: transparent transparent red;
}
`;
export (props) => <ReactSelectElement classNamePrefix="react-select" {...props} />
This applies to react-select#v3.*
I had the same problem and solved it like this:
CustomSelect.js file:
import ReactSelect from 'react-select';
import styled from 'styled-components';
export const CustomSelect = styled(ReactSelect)`
& .Select__indicator Select__dropdown-indicator {
border-color: transparent transparent red;
}
`;
TheComponent.js file:
import React from 'react';
import { CustomSelect } from './CustomSelect';
export function TheComponent () {
return <div>
<CustomSelect
classNamePrefix={'Select'}
{* props... *}
/>
Something awesome here...
</div>
}
`;
Note the classNamePrefix={'Select'} in TheComponent.js - that's important.
This applies to react-select#v1.*
Here you can find an example of styling react-select with styled-components.
To change to caret's colour when the select is opened you can use this
&.Select.is-open > .Select-control .Select-arrow {
border-color: transparent transparent red;
}
The component would look like
import React from 'react';
import ReactSelect from 'react-select';
import styled from 'styled-components';
const RedCaretWhenOpened = styled(ReactSelect)`
&.Select.is-open > .Select-control .Select-arrow {
border-color: transparent transparent red;
}
`;
export (props) => <RedCaretWhenOpened {...props} />

Is it possible to capitalize first letter of text/string in react native? How to do it?

I have to capitalize first letter of text that i want to display. I searched for it but i cant found clear thing to do that, also there is no such props for text in react native official documentation.
I am showing my text with following format:
<Text style={styles.title}>{item.item.title}</Text>
or
<Text style={styles.title}>{this.state.title}</Text>
How can I do it?
Suggestions are welcome?
Write a function like this
Capitalize(str){
return str.charAt(0).toUpperCase() + str.slice(1);
}
then call it from <Text> tag By passing text as parameter
<Text>{this.Capitalize(this.state.title)} </Text>
You can also use the text-transform css property in style:
<Text style={{textTransform: 'capitalize'}}>{this.state.title}</Text>
React native now lets you make text uppercase directly with textTransform: 'capitalize'. No function necessary.
import React from 'react'
import { StyleSheet, Text } from 'react-native'
// will render as Hello!
export const Caps = () => <Text style={styles.title}>hello!</Text>
const styles = StyleSheet.create({
title: {
textTransform: 'capitalize'
}
})
Instead of using a function, a cleaner way is to write this as a common component.
import React from 'react';
import { View, Text } from 'react-native';
const CapitalizedText = (props) => {
let text = props.children.slice(0,1).toUpperCase() + props.children.slice(1, props.children.length);
return (
<View>
<Text {...props}>{text}</Text>
</View>
);
};
export default CapitalizedText;
Wherever you're using <Text>, replace it with <CapitalizedText>
just use javascript.
text.slice(0,1).toUpperCase() + text.slice(1, text.length)
TextInput have this to handle using
autoCapitalize enum('none', 'sentences', 'words', 'characters')
for example try like this
<TextInput
placeholder=""
placeholderTextColor='rgba(28,53,63, 1)'
autoCapitalize = 'none'
value ='test'
/>
this worked for me!
labelStyle:{
textTransform: 'capitalize',
fontSize:20,
},
Since this is very general functionality I put it in a file called strings.js under my library:
// lib/strings.js
export const CapitalizeFirstLetter = (str) => {
return str.length ? str.charAt(0).toUpperCase() + str.slice(1) : str
}
And simply import it in the components that need it:
import React from 'react';
import { View, Text } from 'react-native';
import { CapitalizeFirstLetter} from 'lib/strings'
export default function ComponentWithCapitalizedText() {
return <Text>CapitalizeFirstLetter("capitalize this please")</Text>
}
I just added a prototype function, based on #mayuresh answer
String.prototype.Capitalize = function() {
return this.charAt(0).toUpperCase() + this.slice(1);
}
and use it like this:
myStr.Capitalize()
If you want to capitalize only the first letter of the input:
function CapitalizeFirstLetterOfInput () {
const onInputUppercase = (e) => {
let firstLetter = e.target.value.charAt(0);
e.target.value = firstLetter.toUpperCase() + e.target.value.slice(1);
};
return (
<div>
<input onInput={onInputUppercase}/>
</div>
)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
if anyone interested doing it by just css/style
<strong style={{textTransform: 'capitalize'}}>{props.alert.type}!
here inner {} is for object {textTransform: 'capitalize'}
This answer worked for me:
text.slice(0,1).toUpperCase() + text.slice(1, text.length)
I used it with a props in an alt tag:
`${(props.image).slice(0,1).toUpperCase() + (props.image).slice(1, (props.image).length)}`
Then I guess you can apply that to any text you want.

Resources